Я обнаружил, что это может быть реализовано с помощью:
pbapply::pblapply(list(x), dist)[[1]]
Но это может быть не лучшим образом.
Вместо того, чтобы полагаться на оболочку для выполнения этой работы, положитесь на find, чтобы сделать это:
find . -name "*.foo" -exec somecommand "{}" \;
Затем файл имя будет правильно экранировано и никогда не будет интерпретироваться оболочкой.
You have plenty of answers that explain well how to do it; but for the sake of completion I'll repeat and add to it:
xargs
is only ever useful for interactive use (when you know all your filenames are plain - no spaces or quotes) or when used with the -0
option. Otherwise, it'll break everything.
find
is a very useful tool; put using it to pipe filenames into xargs
(even with -0
) is rather convoluted as find
can do it all itself with either -exec command {} \;
or -exec command {} +
depending on what you want:
find /path -name 'pattern' -exec somecommand {} \;
find /path -name 'pattern' -exec somecommand {} +
The former runs somecommand
with one argument for each file recursively in /path
that matches pattern
.
The latter runs somecommand
with as many arguments as fit on the command line at once for files recursively in /path
that match pattern
.
Which one to use depends on somecommand
. If it can take multiple filename arguments (like rm
, grep
, etc.) then the latter option is faster (since you run somecommand
far less often). If somecommand
takes only one argument then you need the former solution. So look at somecommand
's man page.
More on find
: http://mywiki.wooledge.org/UsingFind
In bash
, for
is a statement that iterates over arguments. If you do something like this:
for foo in "$bar"
you're giving for
one argument to iterate over (note the quotes!). If you do something like this:
for foo in $bar
you're asking bash
to take the contents of bar
and tear it apart wherever there are spaces, tabs or newlines (technically, whatever characters are in IFS
) and use the pieces of that operation as arguments to for. That is NOT filenames. Assuming that the result of a tearing long string that contains filenames apart wherever there is whitespace yields in a pile of filenames is just wrong. As you have just noticed.
The answer is: Don't use for
, it's obviously the wrong tool. The above find
commands all assume that somecommand
is an executable in PATH
. If it's a bash
statement, you'll need this construct instead (iterates over find
's output, like you tried, but safely):
while read -r -d ''; do
somebashstatement "$REPLY"
done < <(find /path -name 'pattern' -print0)
This uses a while-read
loop that reads parts of the string find
outputs until it reaches a NULL
byte (which is what -print0
uses to separate the filenames). Since NULL
bytes can't be part of filenames (unlike spaces, tabs and newlines) this is a safe operation.
If you don't need somebashstatement
to be part of your script (eg. it doesn't change the script environment by keeping a counter or setting a variable or some such) then you can still use find
's -exec
to run your bash
statement:
find /path -name 'pattern' -exec bash -c 'somebashstatement "$1"' -- {} \;
find /path -name 'pattern' -exec bash -c 'for file; do somebashstatement "$file"; done' -- {} +
Here, the -exec
executes a bash
command with three or more arguments.
--
. bash
will put this in $0
, you can put anything you like here, really.{} \;
or {} +
respectively). The filename(s) end(s) up in $1
(and $2
, $3
, ... if there's more than one, of course).The bash
statement in the first find
command here runs somebashstatement
with the filename as argument.
The bash
statement in the second find
command here runs a for
(!) loop that iterates over each positional parameter (that's what the reduced for
syntax - for foo; do
- does) and runs a somebashstatement
with the filename as argument. The difference here between the very first find
statement I showed with -exec {} +
is that we run only one bash
process for lots of filenames but still one somebashstatement
for each of those filenames.
All this is also well explained in the UsingFind
page linked above.
find . -name '*.foo' -print0 | xargs -0 -n 1 somecommand
Тем не менее, может возникнуть проблема, если вам нужно выполнить несколько команд оболочки для каждого элемента.
xargs твой друг. Вы также захотите изучить опцию -0 (ноль) с ним. find
(с -print0
) поможет составить список. На странице Википедии есть несколько хороших примеров.
Еще одна полезная причина для использования xargs
заключается в том, что если у вас много файлов (десятки или больше), xargs разделит их на отдельные вызовы, какими бы ни были xargs. призвал бежать (в первом примере из Википедии rm
)
Некоторое время назад мне приходилось делать нечто подобное, переименовывая файлы чтобы позволить им жить в среде Win32:
#!/bin/bash
IFS=$'\n'
function RecurseDirs
{
for f in "$@"
do
newf=echo "${f}" | sed -e 's/[\\/:\*\?#"\|<>]/_/g'
if [ ${newf} != ${f} ]; then
echo "${f}" "${newf}"
mv "${f}" "${newf}"
f="${newf}"
fi
if [[ -d "${f}" ]]; then
cd "${f}"
RecurseDirs $(ls -1 ".")
fi
done
cd ..
}
RecurseDirs .
Это, вероятно, немного упрощенно, не избегает коллизий имен, и я уверен, что это можно сделать лучше - но это устраняет необходимость использовать базовое имя в результатах поиска (в моем случае) перед выполнением моего замена сед.
Я могу спросить, что вы делаете с найденными файлами?
find . -name '*.foo' -print0 | xargs -0 sh -c 'for F in "${@}"; do ...; done' "${0}"