Search code examples
bashescapingpipeevalquoting

Run piped commands with eval


I have the following command line to check free space of a file system:

fs_used=`df -h /u01 | sed '1d' | sed '1d' | awk '{print $4}' | cut -d'%' -f1`

It works fine. It returns the percentage of the used space on the file system (without the % symbol).

Now I need to make it variable and run it with the eval command. I tried the following but it doesn't work (exit with df: invalid option -- 'd')

df_cmnd="df -h $fs1 | sed '1d' | sed '1d' | awk '{print $4}' | cut -d'%' -f1"
fs_used=eval $df_cmnd

The problem, I guess, is that eval cannot run piped commands. Is that true? is there any workaround or alternative to make this code run?


Solution

  • Backslash-escape the $, and use $():

    #              V                                         V
    df_cmnd="df -h \$fs1 | sed '1d' | sed '1d' | awk '{print \$4}' | cut -d'%' -f1"
    fs_used=$(eval "$df_cmnd")
    #       ^^               ^
    

    This will use the value of fs1 at the time you eval.

    But, in reality, please don't use eval! Make it a shell function instead:

    df_cmnd(){
        df -h "$1" | sed '1d' | sed '1d' | awk '{print $4}' | cut -d'%' -f1
    }
    fs_used=$(df_cmnd /u01)
    

    Then you don't have to worry about escaping.

    Explanation

    Look at how bash interprets your df_cmnd assignment:

    $ df_cmnd="df -h $fs1 | sed '1d' | sed '1d' | awk '{print $4}' | cut -d'%' -f1"
    $ echo $df_cmnd
    df -h | sed '1d' | sed '1d' | awk '{print }' | cut -d'%' -f1
    #    ^                                   ^
    

    In my case, fs1 was empty, so I just got df -h for the df part. In your case and mine, bash replaced $4 with its value, here, empty since I wasn't running in a script with four arguments. Therefore, awk will print the whole line rather than just the fourth field.