How to use wildcards in a xargs-command?
You were very close.
seq 15 -1 1 | xargs -I@ sh -c 'mv @_* @'
You need to delay the interpretation (expansion) of the *
until after the @
substitution has occurred.
(But you already understood that that was the problem, right?)
I’ve been advised never to embed an unknown filename
(or other substitution string) directly into a shell command line.
The above example is probably fairly safe,
because you know what the strings are going to be —
15
, 14
, …, 3
, 2
and 1
.
But using the above example as a template for more complex commands
can be dangerous.
A safer arrangement would be
seq 15 -1 1 | xargs -I@ sh -c 'mv -- "$1"_* "$1"' x-sh @
where x-sh
is a semi-arbitrary string
that will be used to label any error messages issued by the invoked shell.
This is equivalent to my first example, except,
rather than embedding the strings (represented by @
)
directly into the shell command,
it injects them by supplying the @
as an argument to the shell,
and then referencing them as "$1"
.
P.S. You suggested running the seq
command in reverse
(seq 15 -1 1
, which generates 15
, 14
, …, 3
, 2
, 1
rather than 1
, 2
, 3
, …, 14
, 15
) and nobody mentioned it.
This would be an important part of the answer
if your filenames were like 1foo.txt
, 2bar.asc
, and 13test.png
, etc. (with various characters appearing after the number, rather than always _
).
In that case, the command would be mv "$1"* "$1"
(without the _
),
and, if you did 1
first, then the command mv 1* 1
would sweep up all the 10quick*
, 11brown*
, 12fox*
, etc., files,
along with the 1foo*
files.
But
seq 1 15 | xargs -I@ sh -c 'mv -- "$1"_* "$1"' x-sh @
should be safe.
P.P.S. The seq
command is not specified by POSIX.
Neither is brace expansion in the shell.
A POSIX-compliant solution can be constructed
by combining grawity’s answer to this question
with this other answer by Adam Katz:
i=1
while [ "$i" -le 15 ]
do
mv -- "${i}"_* "$i"
i=$((i+1))
done
P.P.P.S. It’s not critical when you know that the file names
begin with alphanumeric characters (i.e., letters and digits),
but, in more general cases,
you should use --
between the command name and the arguments.
This improves the handling of filenames that begin with a dash.
The --
tells the command to treat the argument (the file name)
as an argument.
Without it, such an argument might be treated as an option string.
Just don't use xargs
for that. Use a for
loop:
for i in $(seq 1 15); do
mv ${i}_* $i
done
Even better is to use brace expansion instead of seq
:
mkdir {1..15}
for i in {1..15}; do
mv ${i}_* $i
done