Search code examples
bashunixxargsglob

Reading an argument list with a glob from a file


I am trying to read arguments of ll command from file. I have file args.txt with content some?. I have files with names some1, some2 and some3 and some other files. when I execute cat args.txt | ll I get the same result as when I execute ll.

Can someone explain me why is that and how can I achieve the desired result. Thanks in advance.


Solution

  • Note: I'm assuming that ll is an alias for ls -l or some variation.
    As Charles Duffy points out, (by default) only interactive shells know about aliases, and scripts as well as tools such as xargs are unaware of them.

    What you send through a pipeline to a command is received via its stdin stream, which is distinct from a command's arguments (options such as -l and operands such as some?).

    Therefore, an explicit transformation is needed to convert the contents of file args.txt to an argument you can pass to ls -l:

    ls -l $(cat args.txt)   # In Bash you can simplify to: ls -l $(< args.txt)
    

    Note that the command substitution ($(...)) is deliberately unquoted so as to ensure that globbing (filename expansion) is applied to the contents of args.txt, as desired.

    Yours is a rare case where this technique - which is brittle - is actually needed.

    To illustrate the brittleness: If your globbing pattern were "some file"?, for instance (you'd need the double quotes for the pattern to still be recognized as a single argument), the command wouldn't work anymore, because the " characters lose their syntactic function when they're the result of a command substitution (or variable expansion).


    The standard utility for transforming stdin or file content into arguments is xargs. However, in your particular case it is not an option, because your file contains a glob (filename pattern) that must be expanded by the shell, but xargs only invokes external utilities, without involving a shell:

    $ xargs ls -l < args.txt # !! Does NOT work as intended here.
    ls: file?: No such file or directory
    

    Filename file? was passed literally to ls (and no file actually named file? exists) - no globbing happened, because no shell was involved.