jbrown
jbrown

Reputation: 7996

Executing a string as a command in bash that contains pipes

I'm trying to list some ftp directories. I can't work out how to make bash execute a command that contains pipes correctly.

Here's my script:

#/bin/sh

declare -a dirs=("/dir1" "/dir2")           # ... and lots more
for d in "${dirs[@]}"
do
    cmd='echo "ls /mydir/'"$d"'/*.tar*" | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1'
    $cmd
done

This just outputs:

"ls /mydir/dir1/*.tar*" | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1
"ls /mydir/dir2/*.tar*" | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1

How can I make bash execute the whole string including the echo? I also need to be able to parse the output of the command.

Upvotes: 2

Views: 4523

Answers (3)

Tom Fenech
Tom Fenech

Reputation: 74695

I don't think that you need to be using the -b switch at all. It should be sufficient to specify the commands that you would like to execute as a string:

#/bin/bash

dirs=("/dir1" "/dir2")
for d in "${dirs[@]}"
do
    printf -v d_str '%q' "$d"
    sftp -i ~/mykey [email protected] "ls /mydir/$d_str/*.tar*" 2>&1 | tail -n1       
done

As suggested in the comments (thanks @Charles), I've used printf with the %q format specifier to protect against characters in the directory name that may be interpreted by the shell.

Upvotes: 3

chepner
chepner

Reputation: 532313

Don't store the command in a string; just use it directly.

#/bin/bash

declare -a dirs=("/dir1" "/dir2")           # ... and lots more
for d in "${dirs[@]}"
do
  echo "ls /mydir/$d/*.tar*" | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1
done

Usually, people store the command in a string so they can both execute it and log it, as a misguided form of factoring. (I'm of the opinion that it's not worth the trouble required to do correctly.)

Note that sftp reads from standard input by default, so you can just use

echo "ls ..." | sftp -i ~/mykey [email protected] 2>&1 | tail -n1

You can also use a here document instead of a pipeline.

sftp -i ~/mykey [email protected] 2>&1 <<EOF | tail -n1
ls /mydir/$d/*.tar.*
EOF

Upvotes: 1

anubhava
anubhava

Reputation: 786091

First you need to use /bin/bash as shebang to use BASH arrays.

Then remove echo and use command substitution to capture the output:

#/bin/bash

declare -a dirs=("/dir1" "/dir2")           # ... and lots more
for d in "${dirs[@]}"
do
    output=$(ls /mydir/"$d"/*.tar* | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1)
    echo "$output"
done

I will however advise you not use ls's output in sftp command. You can replace that with:

output=$(echo "/mydir/$d/"*.tar* | sftp -b - -i ~/mykey [email protected] 2>&1 | tail -n1)

Upvotes: 1

Related Questions