1

Here's what I'm trying to do (in a script):

#!/usr/bin/env bash

if [[ ! $("/usr/bin/scp [email protected]:/Users/seamus/Downloads/imgutils/image-utils* /home/pi/testscp") ]]; then
    printf "\nERROR & EXIT: 'scp' failed \n"
    exit 1
fi
printf "success!\n"

I've tried doing this in a few ways, but nothing seems to work. Here's what I get from the above:

./testscp.sh: line 3: /usr/bin/scp [email protected]:/Users/seamus/Downloads/imgutils/image-utils* /home/pi/testscp: No such file or directory

ERROR & EXIT: 'scp' failed

The scp command runs OK when it's on a line by itself. If I do that, I can test for $0 - which works OK... but I'd prefer to do it all in a single line. What am I missing?

2 Answers 2

10

Use if on its own, and quote the argument containing the glob intended for scp:

if ! /usr/bin/scp '[email protected]:/Users/seamus/Downloads/imgutils/image-utils*' /home/pi/testscp; then
    printf>&2 "\nERROR & EXIT: 'scp' failed \n"
    exit 1
fi
printf "success!\n"
12
  • Thanks to you both (incl Stéphane). I don't think I'd have ever thought of eliminating test! Can you provide a reference for that? Commented Nov 26, 2024 at 17:12
  • 3
    A reference saying not to use [[? I’m not aware of anything like that. Would the definition of if in the Bash manual be useful? See also the POSIX spec for if. Commented Nov 26, 2024 at 17:27
  • 2
    I understand the feeling! If you’re used to if [ … ]; then or if [[ … ]]; then it can feel like that’s just how if is used (and that matches how it’s used in other programming languages). But with the shell, it can be decomposed — [ is itself a command, and any command can be used with if. Basically, whenever you want to check $?, instead of doing command; if [ $? -ne 0 ]; then …, if command; then works just as well. Commented Nov 27, 2024 at 6:42
  • 1
    The test command implements specific types of tests. Just because any command can be a test-command doesn’t mean that all of a sudden every type of test is implemented — any command can be a test-command, but you need commands implementing the tests you want. Commented Nov 27, 2024 at 18:26
  • 1
    @Seamus It is true. test exists because it can check things like "are these two strings equal?" or "does this file exist and is it writable for me?", which mkdir and scp can't do for you. test can check for a whole barrage of useful things, which is why it's frequently used together with if. But again, you can use any command, and there's many scenarios (such as yours) where another command is more appropriate than test. Commented Nov 28, 2024 at 10:32
4

In bash, quotes are used to delimit a SINGLE label which would otherwise be interpreted as multiple labels (due to whitespace characters). So when bash reads:

if [[ ! $("/usr/bin/scp [email protected]:/Users/seamus/Downloads/imgutils/image-utils* /home/pi/testscp") ]]

It interprets this as:

if [[ ! $( LONG_EXECUTABLE_NAME_WITH_FUNNY_CHARACTERS ) ]]; then

Your quotes are in the wrong place.

In order to decompose that into an executable, there are multiple ways this could be written, but simply:

if [[ ! $(/usr/bin/scp [email protected]:/Users/seamus/Downloads/imgutils/image-utils* /home/pi/testscp) ]]; then

would suffice.

But as @StephenKitt says, the combination of test ([[...]]) and expansion ($(...)) is redundant. Indeed (as ikkachu points out below) this is testing for a non-null response on stdout, NOT the exit code of scp.

but I'd prefer to do it all in a single line.

That's a question of style rather than functionality, but I suggest it is not good style to write scripts with very long lines.

2
  • 2
    it's just that if [[ ! $(/usr/bin/scp ...) ]]; would test if scp's output is empty, which likely isn't the best way to test if it worked. Commented Nov 26, 2024 at 15:39
  • Good point! I was too focussed on the escaping issue. Commented Nov 26, 2024 at 17:11

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.