Search code examples
shellpathname

what is returned after pathname expansion in shell?


1、a string separated by white space?
If I execute rm -r *.txt where there is a lot of TXT file,I get an "Argument list too long" error,it seems to tell me that it equasl to rm -r a.txt b.txt...c.txt after pathname expansion.
2、just a list object used by the specific command?
If I execute tar --exclude *.txt a.tar.gz a where there are two TXT files ("a.txt" and "b.txt") ,the result is not the same as executing tar --exclude a.txt b.txt a.tar.gz a.According to that,maybe we can get the conclusion that just a list object is returned,not a string separated by white space.

I have a dir named a,the structure is like: a ├── a.txt ├── b.txt └── cc

If I execute tar --exclude *.txt -czvf a.tar.gz a,I get result as: a/ a/cc

"a.tar.gz" is generated successfully without *.txt file. If I execute tar --exclude a.txt b.txt -czvf b.tar.gz a,I get result as: tar: b.txt:cat not stat: no such file or directory a/ a/cc a/b.txt

"b.tar.gz" is generated without a.txt and with cc and b.txt

@Jan Hudec


Solution

  • Neither. It returns a list of strings.

    Word splitting is performed before filename generation, so at that point, shell already works with a list of strings that will form the command. The generated filenames are inserted, as a separate entry each, into that list.

    If I execute rm -r *.txt where there is a lot of TXT file,I get an "Argument list too long" error,it seems to tell me that it equasl to rm -r a.txt b.txt...c.txt after pathname expansion.

    So, yes, it is. And if some of the files has space in its name, it will be passed correctly (which it wouldn't if the pattern returned a space-separated string). However:

    If I execute tar --exclude *.txt a.tar.gz a where there are two TXT files ("a.txt" and "b.txt") ,the result is not the same as executing tar --exclude a.txt b.txt a.tar.gz a.

    You forgot to mention the files are in the a directory. Therefore the *.txt will not expand to a.txt, b.txt, because there are no such files—there are only a/a.txt and a/b.txt. And here comes a quick of bash (zsh can be configured either way): a pattern that does not match anything is left as is. So

    tar --exclude *.txt a.tar.gz a
    

    expands to just tar, --exclude, *.txt, a.tar.gz and a. And tar does it's own wildcard processing for the parameter to --exclude option.

    However, if there were also c.txt and d.txt in the current directory, it would expand to tar, --exclude, c.txt, d.txt, a.tar.gz and a and break down. To prevent that, quote the pattern:

     tar --exclude '*.txt' a.tar.gz a