Bash – Using | pipe character from a $variable makes it treat as just another argument in bash; how to escape it

bashpipequotingshellvariable

I have a bash script like this

export pipedargument="| sort -n"
ls $pipedargument

But it gives the error

ls: |: No such file or directory
ls: sort: No such file or directory

It seems to be treating the contents of "| sort -n" as just an argument passed to ls.

How can I escape it so that it's treated as a regular piped command?

I'm trying to conditionally set the $pipedargument. I guess I could just conditionally execute different versions of the command but still wondering if there's a way to make this work like above?

Best Answer

You are right that you cannot use | that way. The reason is that the shell has already looked for pipelines and separated them into commands before it does the variable substitution. Hence, | is treated as just another character.

One possible work-around is to place the pipe character literally:

$ cmd="sort -n"
$ ls | $cmd

In the case that you don't want a pipeline, you can use cat as a "nop" or placeholder:

$ cmd=cat
$ ls | $cmd

This method avoids the subtleties of eval. See also here.

A better approach: arrays

A more sophisticated approach would use bash arrays in place of plain strings:

$ cmd=(sort -n)
$ ls | "${cmd[@]}"

The advantage of arrays becomes important as soon as you need the command cmd to contain quoted arguments.