0

I have copied some files from one user-account to a new one by these commands

$ folders_array=(.mozilla .thunderbird .ssh .eclipse)

$ echo ${folders_array[@]} | xargs -d " " -I _ sudo cp -r _ /home/nebukadnexa/

I can't explain the form of the last argument .eclipse which is, that it is '.eclipse'$'\n' and thus wasn't found in my current home directory and thus also wasn't copied of course.

Can you point me to how xargs produces the argument '.eclipse'$'\n' (being the last argument)?

EDIT: Maybe a better solution than -d " " is tr " " "\n" ?

echo ${folders_array[@]} | tr " " "\n" | xargs -I _ printf "%s\n" _
von spotz
  • 875
  • 7
  • 17
  • 1
    Why are you using `-d " "`? By default xargs treats any whitespace as a delimiter. – Barmar May 18 '21 at 15:57
  • @Barmar It's not reliably portabde to assume that; see the OP's earlier question today. – tripleee May 18 '21 at 15:58
  • @Barmar Normally it should if you don't put the parameter expansion of the array into double quotes. But look at this thread of mine where it doesn't https://stackoverflow.com/questions/67584696/bash-xargs-how-to-mass-assign-user-privileges – von spotz May 18 '21 at 15:58
  • 1
    The hack proposed in your edit exacerbates the existing problem; you now can't support fileenames with spaces in them even if you fix the quoting. IMHO, a move in the wrong direction. – tripleee May 18 '21 at 16:02
  • 1
    @tripleee `-d` is also not POSIX, see https://pubs.opengroup.org/onlinepubs/009604599/utilities/xargs.html. As far as I can tell from that, unquoted spaces should be treated as argument delimiters. – Barmar May 18 '21 at 16:05
  • 1
    The problem you're having in your previous question is that `usermod` only allows you to specify one group at a time, but `xargs` tries to put them all in the same command unless you use `-n 1`. – Barmar May 18 '21 at 16:07
  • 1
    It's the difference between `echo "${array[@]}" | xargs echo` and `echo "${array[@]}" | xargs -n 1 echo` – Barmar May 18 '21 at 16:10
  • 1
    I see the other problem: When you use `-I _`, the `_` in the input is substituted as a single argument. The spaces lose their significance. – Barmar May 18 '21 at 16:15

3 Answers3

4

When you set -d " " the newline at the end of the echo output is considered part of the data, not a delimiter.

The absolutely simplest fix is to not provide the input on a single line. (Notice also the fixed quoting of the array.)

printf '%s\n' "${folders_array[@]}" |
xargs -r sudo cp -t -r /home/nebukadnexa/

I also switched to cp -t which exists precisely so that you can simplify cases like this. This is a GNU extension, so it's not necessarily portable to non-Linux platforms (though it's not hard to write your own reimplementation, either).

If you need to support completely arbitrary file names (even file names which contain newlines), printf '%s\0' piped to xargs -0 is good to know, though again, that's a GNU extension.

tripleee
  • 175,061
  • 34
  • 275
  • 318
2

The problem is that when you use -I _, it replaces _ as a single argument. POSIX specifies this (emphasis mine):

-I replstr
Insert mode: utility is executed for each line from standard input, taking the entire line as a single argument, inserting it in arguments for each occurrence of replstr.

This is why the GNU cp -t extension is useful. It allows you to put the files to copy at the end of the command, which is where xargs puts them by default, without combining them into a single argument.

echo "${folders_array[@]}" | xargs sudo cp -r -t /home/nebukadnexa/

Note that piping to xargs won't work if any of the filenames contain whitespace.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Hello Barmar, couldn't I use the `-n` option on xargs to feign off `combining them into a single argument.` ? And doesn't putting the parameter-expansion `${folders_array[@]}` in double quotes (according to this answer https://unix.stackexchange.com/a/26672/440130 in this thread https://unix.stackexchange.com/questions/26661/what-is-word-splitting-why-is-it-important-in-shell-programming) also prevent `field splitting`, thus lumping all arguments together in one singular string ? Cheers ! – von spotz May 18 '21 at 18:53
  • 1
    Yes to the first question, no to the second. – Barmar May 18 '21 at 18:56
  • 2
    When you put quotes around an array with `[@]`, it requotes each array element, not the whole array. – Barmar May 18 '21 at 18:56
  • Thanks for the answer ! – von spotz May 18 '21 at 18:57
  • 1
    But if you use `-n 1` you lose the benefit of running a single `cp` command for all the files. – Barmar May 18 '21 at 18:57
  • What do I have to do to compensate? – von spotz May 18 '21 at 19:03
  • 1
    Do it as in my answer. – Barmar May 18 '21 at 19:05
1

Not sure I see the benefit of xargs here anyway.

for d in .mozilla .thunderbird .ssh .eclipse
do sudo cp -r "$d" /home/nebukadnexa/
done
Paul Hodges
  • 13,382
  • 1
  • 17
  • 36