I am attempting to create a directory using the command mkdir
. However, I would like to move a subset of files into that directory. I understand I can use xargs, however my attempts have failed. For example, I have tried mkdir test | xargs -i mv test.text {}
. It creates the directory, but it does not move the file test.txt
into it after it has been created.
Linux – Using xargs with mv and mkdir command in Linux
linuxxargs
Related Solutions
No.
The cp
command does not possess the capability to process any of its arguments as regular expressions. Even wildcards are not handled by it (or most executables); rather they are handled by the shell.
cp test/* test2/
is actually expanded by bash, and all that cp
really sees for its arguments are cp test/file1 test/file2 test/file3 test2/
(or whatever is appropriate based upon the actual contents of test/
).
Also, I don't think your expression, [^\.php]
, will match what you want it to (it doesn't match only files that contain .php
).
You probably want to look into the find
utility to filter out a list of files based upon regular expression, and then use xargs
to apply the returned file list to the cp
command (presuming find doesn't have a built-in handler for copying files; I'm not intimately familiar with the tool).
You might try:
find . ! -iregex ".*\.php.*" -exec cp {} /destination/folder/ \;
This says to search the current directory recursively for files which do not contain ".php" in the path and copy them into /destination/folder/
.
As requested, a more specifc breakdown of the arguments:
.
- Location to start the search - in this case, the current directory!
- "Not" operator, invert the result of the next test-iregex
- Case-insensitive regular expression test. Next argument is the expression.".*\.php.*"
- Regular expression matching<Anything>.php<Anything>
- Any file that has ".php" somewhere in the path. (Note, including being inside a folder which contains ".php" in the name, you'd need a more complex expression to match only the files)-exec
- Execute a command if the preceding tests return true. Next argument is the command, all remaining arguments up to;
are passed to the command. the{}
is a special argument representing the filename.cp
- The command that find` should run on each of the matched path names.{}
- The path to the file that was found, passed tocp
as an argument so that it knows what file to copy/destination/folder/
- argument passed tocp
, tellingcp
where it should copy the file to.\;
- This is the;
terminator that-exec
is looking for. We escape it with a\
so that your shell doesn't try to parse it and instead feeds it as an argument to the command (find
)
It's rather difficult to write a regular expression which matches "strings that do not have .php
", so we instead tell find
to search for strings that do contain .php
and then invert the results with !
.
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.
Best Answer
mkdir
does not output anything, thereforexargs
will not do anything useful. I don't understand exactly what you want to do, so you should explain your question better."would like to move a subset of files into that directory" -> do files in this subset share one or more traits? If yes, use the find command like this:
If you want to avoid typing the name of the directory twice, or if you are doing this from a script, you can do something like