Score:2

Why this func in xargs has error?

ht flag

Why this coammand has error? get_year func works.

xargs -I {} -r -n1 cp {} ${Dst}/videos/$(bash -c 'get_year {}')

errors

ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory
ls: cannot access '{}': No such file or directory

get_year func

get_year() {
ls -l --time-style=full $1 | tr -s ' ' | cut -d' ' -f6 | cut -d'-' -f 1 

}
export -f get_year
Score:3
jp flag

Why?

The place holder/replace string e.g. {} should get replaced after the xargs command, even if improperly placed inside a single quoted Bash command string (which can be dangerous ... See the remarks part below for explanation) that will run in a sub-shell like e.g.:

$ printf '%s\n' 'line1' 'line2' 'line3' | xargs -I {} -r -n1 bash -c 'echo "This is {}"'
This is line1
This is line2
This is line3

Although, the proper and safer way is to pass it to the Bash script as a positional parameter like so:

$ printf '%s\n' 'line1' 'line2' 'line3' | xargs -I {} -r -n1 bash -c 'echo "This is $1"' bash {}
This is line1
This is line2
This is line3

That said, if you enclose the Bash script in a command substitution($( ... )) syntax, then the command substitution part gets executed before the xargs command i.e. before the place holder {} is assigned any value and therefore, it will be processed as a literal {}.

If you enable command traces in Bash like so:

$ set -x

Then run without command substitution:

$ printf '%s\n' 'line1' 'line2' 'line3' | xargs -I {} -r -n1 bash -c 'echo "This is {}"'
+ xargs -I '{}' -r -n1 bash -c 'echo "This is {}"'
+ printf '%s\n' line1 line2 line3
This is line1
This is line2
This is line3

And then, with command substitution:

$ printf '%s\n' 'line1' 'line2' 'line3' | xargs -I {} -r -n1 $(bash -c 'echo "This is {}"')
+ printf '%s\n' line1 line2 line3
++ bash -c 'echo "This is {}"'
+ xargs -I '{}' -r -n1 This is '{}'
xargs: This: No such file or directory

It should be obvious to you now that the xargs command is trying to execute and pass arguments to the output of the command substitution, because command substitution gets expanded early on the command line in Bash.

How?

Although moving the command substitution part inside the Bash command string so it will be run in a sub-shell(Command substitution) from withing another sub-shell(Bash command string) from within anothr sub-shell(created for that part of the pipe line) ... Therefore, delaying its execution(away from the perspective of the current shell's command line parsing) until the place holder {} has been assigned a value by xargs ... Although that might work, but I would rather not do it.

Not discussing your obvious parsing of ls's output in your function and the probable piping of similar output by ls as well which I'd rather not recommend ... Please see more explanation about that here.

What I would recommend instead is to not use a pipeline and not use xargs, but rather use a shell loop to process your files like so:

for f in *; do
  if [ -f "$f" ]; then
    y=$(get_year "$f")
    cp -v -- "$f" "${Dst}"/videos/"$y"
    fi
  done

Remarks

  • Probably Too Technical Notice: The place holder/replace string in xargs option -Ie.g. {} in your case is not actually expanded per-se i.e. by the shell, but rather replaced by xargs and its replacement put in "the same" position on the command line itself by writing to standard output ... That can be observed with strace like e.g. so:

    $ printf '%s\n' 'line' | strace -f -e 'write' xargs -I {} echo {}
    strace: Process 37373 attached
    [pid 37373] write(1, "line\n", 5line
    )       = 5
    [pid 37373] +++ exited with 0 +++
    --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=37373, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
    +++ exited with 0 +++
    

    ... And that's why it could be dangerous to include the place holder/replace string as is in shell scripts as it will allow for command injection which could be malicious or destructive.

    Compare for example:

    $ printf '%s\n' '; seq 1 9' '; echo ~' | xargs -I {} bash -c 'echo {}'
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    /home/ubuntu
    

    ... where commands get executed e.g. seq 1 9 and shell expansion happens e.g. ~ expanded to your home directory ... Imagine what could happen if, for example, one of the injected commands happened to be ; rm -rf ~

    Compare that with when the place holder/replace string is properly passed to the shell script as a positional parameter:

    $ printf '%s\n' '; seq 1 9' '; echo ~' | xargs -I {} bash -c 'echo "$1"' bash {}
    ; seq 1 9
    ; echo ~
    

    ... where no injected commands get executed and no shell expansion happens.

  • The options -I and -n are mutually exclusive so you might not want to use them together although if the argument to -n is 1 then probably no harm will be done ... Also -I implies -L 1 so you might not need to use -n 1 at all.

Score:0
cn flag

The error you're encountering is due to the placeholder {} not being expanded by xargs. To resolve this issue, you need to use the -I option correctly with xargs. The placeholder {} will be replaced by the input arguments passed to xargs so it will look like this:

xargs -I {} -r -n1 bash -c 'cp "{}" "${Dst}/videos/$(get_year "{}")"' -- {}
muru avatar
us flag
Do not embed `{}` directly into shell code. That'll create a code injection vulnerability. See, e.g., https://unix.stackexchange.com/a/448449/70524 or https://unix.stackexchange.com/q/156008/70524
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.