This is a follow up to a question posted on SO.

I've written a script called except which should print all filenames / directories except the ones given like so.

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

The script I've written so far looks like this

shopt -s extglob
shopt -s nullglob

IFS=' '

printf '%s' $matches

This works more or less but the main problem is that printf doesn't seem to respect IFS set to (blank).

It just prints them without a space between. If I instead use echo, it works (except for the added newline). But for compatibility reasons I want to use printf for this task. How can I achieve this? What am I doing wrong?

I also tried using the %q modifier which also didn't result in the desired output.

printf '%q' $matches
  • What about leaving out IFS and use just printf '%s ' $matches? (I mean include a literal space in the format string.)
    – manatwork
    Feb 12, 2013 at 10:15
  • @manatwork While that would work I don't want the extra blank behind the last match (I know I'm nitpicking here). Feb 12, 2013 at 10:19
  • printf '%s' $something : will print the content of $something, with nothing around it. So it's just what you ask it to do... And use quotes (") to save and restore IFS, otherwise it won't be the exaxt content. ( OLDIFS="$IFS" ; .... ; IFS="$OLDIFS" ; ...) Feb 12, 2013 at 10:57

1 Answer 1

$ ls
a b c d
$ rm $(except b d)
$ ls
b d

That can't work, at least not reliably, because "except" cannot decide how the shell will split (and further expand) its output before passing it to the rm command.

To answer your question, printf doesn't use $IFS. The only command that uses IFS is read. Other than that, $IFS is used by the shell for word splitting and to join the positional arguments in "$*".

You probably want:

 printf '%s ' $matches


 printf '%s\n' $matches

But again, in

 rm $(except a b)

The shell that interprets that command line will split the output of except according to its own $IFS (by default space, tab or newline, which is why using space or \n above makes no difference) and will also expand the wildcards in there.

So if the files other than a and b are "this file.txt" and **a**, except will output "this file.txt **a** " and you will end up trying to remove this, file.txt and a (since **a** will expand to all the files with a in their name).

If you want to join the matches with spaces without the trailing space, you could do:

set -- $matches
IFS=' '

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.