7

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?

7

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.

  • Could you clarify why arrays are needed in case of quoted arguments? I ran into that, but don't quite understand why it doesn't work. – anxieux Mar 16 '17 at 12:21
  • @anxieux A short answer is that a quote, when inside a shell string, loses all its syntactical power to group words and is instead treated like just any other character. For a longer and excellent discussion of this issue, see: I'm trying to put a command in a variable, but the complex cases always fail!. – John1024 Mar 16 '17 at 19:03
5

You can evaluate the command:

eval "ls $pipedargument"

or even better define function like:

sorted() { "$@" | sort -n; }

and later on call it with desired arguments:

sorted ls /tmp
  • Another option would be to define an alias: alias ls='ls | sort -n' – thiagowfx Jan 5 '15 at 5:00
0

I would use a function for this. Something like:

### usage pipedargument cmd args ###

pipedargument()
{
    sort -n <<< "$( "$@" )"
}

$ pipedargument /sbin/ifconfig eth0
      RX bytes:5904986765 (5.4 GiB)  TX bytes:714370767 (681.2 MiB)
      RX packets:5981427 errors:0 dropped:0 overruns:0 frame:0
      TX packets:4403989 errors:0 dropped:0 overruns:0 carrier:0
      UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
      collisions:0 txqueuelen:1000 
      inet addr:XXX.XXX.X.XX  Bcast:XXX.XXX.X.XXX  Mask:255.255.255.0
      inet6 addr: xx00::0x0x:00xx:xx0:000/00 Scope:Link
eth0      Link encap:Ethernet  HWaddr 0x:0x:00:x0:00:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.