How to Make a Bash String of Command with Redirect and Pipe

How do I redirect output to a variable in shell?

Use the $( ... ) construct:

hash=$(genhash --use-ssl -s $IP -p 443 --url $URL | grep MD5 | grep -c $MD5)

bash exec sending output to a pipe, how?

When a command contains a pipeline, each subcommand is run in a subshell. So the shell first forks a subshell for each part of the pipeline, and then the subshell for the first part executes exec with no arguments, which does nothing and exits.

exec with redirection and no command is treated as a special case. From the documentation:

If command is not specified, any redirections take effect in the
current shell, and the return status is 0.

Bash: execute content of variable including pipe

Using eval is not recommended here. It can lead to unexpected results, especially when variables can be read from untrusted sources (See BashFAQ/048 - Eval command and security issues.

You can solve this in a simple way by defining and calling a function as below

ps_cmd() {
ps aux | grep -v grep
}

and use it in the script as

output="$(ps_cmd)"
echo "$output"

Also a good read would be to see why storing commands in a variable is not a good idea and has a lot of potential pitfalls - BashFAQ/050 - I'm trying to put a command in a variable, but the complex cases always fail!

Send string to stdin

You can use one-line heredoc

cat <<< "This is coming from the stdin"

the above is the same as

cat <<EOF
This is coming from the stdin
EOF

or you can redirect output from a command, like

diff <(ls /bin) <(ls /usr/bin)

or you can read as

while read line
do
echo =$line=
done < some_file

or simply

echo something | read param

Pipe a command and redirect the output with command

This is a simplified version of how you would achieve this:

outfile, _ := os.OpenFile("backup.gz", os.O_RDWR|os.O_CREATE, 0755)

// your zfs command
zfs := exec.Command("zfs", "send", "storedb@backup")
gzip := exec.Command("gzip", "-cf") // gzip to stdout (-cf)
gzip.Stdin, _ = zfs.StdoutPipe() // zfs stdout to gzip stdin
gzip.Stdout = outfile // write output of gzip to outfile
gzip.Start() // gzip start waiting for input
zfs.Run() // zfs start command
gzip.Wait() // gzip wait for pipe to close
outfile.Close()

It is equivalent to this command in the shell:

zfs send stored@backup | gzip -cf > backup.gz

Piping and Redirection

Redirection is (mostly) for files (you redirect streams to/from files).

Piping is for processes: you pipe (redirect) streams from one process to another.

Essentially what you really do is "connect" one standard stream (usually stdout) of one process to standard stream of another process (usually stdin) via pipe.

Pipes have also the synchronization "side effect" : they block one process (on reading) when the other has nothing to write (yet) or when reading process cannot read fast enough (when the pipe's buffer is full).

How to redirect output of command to a file and in parallel pipe to other command?

You can use tee

ls | tee output.txt | wc -l

Pipe Bash command output to stdout and to a variable

Copying To A TTY (Not Stdout!)

Pipeline components run in subshells, so even if they do assign shell variables (and the syntax for that was wrong), those shell variables are unset as soon as the pipeline exits (since the subshells only live as long as the pipeline does).

Thus, you need to capture the output of the entire pipeline into your variable:

var=$(find "$filename" -perm "$i" | tee /dev/tty | wc -l)

Personally, btw, I'd be teeing to /dev/stderr or /dev/fd/2 to avoid making behavior dependent on whether a TTY is available.


Actually Piping To Stdout

With bash 4.1, automatic file descriptor allocation lets you do the following:

exec {stdout_copy}>&1 # make the FD named in "$stdout_copy" a copy of FD 1

# tee over to "/dev/fd/$stdout_copy"
var=$(find "$filename" -perm "$i" | tee /dev/fd/"$stdout_copy" | wc -l)

exec {stdout_copy}>&- # close that copy previously created
echo "Captured value of var: $var"

With an older version of bash, you'd need to allocate a FD yourself -- in the below example, I'm choosing file descriptor number 3 (as 0, 1 and 2 are reserved for stdin, stdout and stderr, respectively):

exec 3>&1  # make copy of stdout

# tee to that copy with FD 1 going to wc in the pipe
var=$(find "$filename" -perm "$i" | tee /dev/fd/3 | wc -l)

exec 3>&- # close copy of stdout


Related Topics



Leave a reply



Submit