Как иметь дело с пробелами в переменной

У Общества Наследия Unix есть загрузка старого Подлинного исходного кода Unix. Страница исходного кода имеет Unix Bell Labs от Версии 1 - 6, 32V, Система III, некоторые или все BSDs, указатели на прародителя Linux Minix.

5
14.06.2013, 02:04
5 ответов

Самый безопасный путь состоит в том, чтобы использовать globbing:

for file in *pdf; do echo pathname "$file"; done

Если необходимо найти весь PDF рекурсивно, сделайте это:

shopt -s globstar
for file in **/*pdf; do echo pathname "$file"; done
4
27.01.2020, 20:31
  • 1
    К сожалению, bash **/ следует за символьными ссылками при убывании дерева каталогов, которое обычно является не, что Вы хотите, можно хотеть использовать ksh93один (который bash скопированный это с и не имеет проблемы), или еще лучше zshодин (который ksh93 скопированный это с), где у Вас может быть большая часть функциональности find через его globbing спецификаторы. –  Stéphane Chazelas 13.06.2013, 22:14

Как сказано многие время на этом сайте, оставляя переменное расширение (как в $var) или замена команды (как в `cmd` или $(cmd)) (или арифметическое расширение (как в $((11 * 11))) в большинстве оболочек), закрыл кавычки в оболочках Границы/POSIX, split+glob оператор.

Содержание $var или вывод cmd (без запаздывающих символов новой строки), разделяется согласно текущему значению $IFS специальная переменная (который по умолчанию содержит SPC, ВКЛАДКУ и символы NL (и NUL в zsh)) и каждое слово, получающееся из того разделения, подвергается поколению имени файла, также известному как globbing.

Например, если find ouputs ./foo bar.pdf\n./*foo*\tbar.pdf\n (\t значение ВКЛАДКИ и \n NL), со значением по умолчанию $IFS, замена команды расширится до ./foo bar.pdf\n./*foo*\tbar.pdf (запаздывание удаленной новой строки), и затем быть разделенным на ./foo, bar.pdf, ./*foo*, и foo.pdf и ./*foo* то, которое является подстановочным шаблоном, будет расширено в столько же аргументов, сколько существуют нескрытые файлы в текущем каталоге, имя которого содержит foo.

Если Вы хотите разделить на символах новой строки только, необходимо установить $IFS к новой строке только:

IFS='
'

Если Вы не хотите, чтобы подстановочные шаблоны были расширены, необходимо отключить его с

set -f

Однако обратите внимание, что новая строка является столь же допустимым символом как любой в имени файла, так в более общем плане, find -print вывод не может быть выполнен последующую обработку надежно.

Вывод как:

./a.pdf
./b.pdf

любое средство a.pdf и b.pdf файлы в текущем каталоге или названный файл b.pdf в a.pdf\n. каталог.

Некоторые find реализации как GNU find (где это произошло из), имеют a -print0 предикат для вывода имени файла, сопровождаемого символом NUL вместо символа NL. Со стандартом find, можно использовать -exec printf '%s\0' {} + с тем же результатом. NUL является единственным символом, который не может произойти в имени файла.

Однако zsh единственная оболочка, которая может сохранить символ NUL в его переменных (как $IFS символ), таким образом:

IFS=$'\0'
for i in `find ... -print0`; do
  ...
done

(никакая потребность в set -f в zsh с тех пор zsh не делает globbing на замену команды), будет работать в zsh но не в других оболочках.

Лучше всего, и портативно, должен иметь find назовите команды, Вы хотите работать на тех файлах. Поскольку @Gnouc предлагает:

find . -name '*.pdf' -exec the command {} \;

При необходимости в чем-нибудь более сложные операторы оболочки вовлечения можно все еще сделать вещи как:

find . -name '*.pdf' -exec sh -c '
  for i do
    something complex with "$i"
  done' sh {} +

С zsh или bash, можно также сделать:

find . -name '*.pdf' -print0 |
  while IFS= read -r -d '' file; do
    whatever with "$file"
  done

Однако обратите внимание, что stdin в цикле затронут.

zsh (с 1990) имеет большую часть функциональности find включенный в его globbing возможности через синтаксис, где можно указать любой уровень подкаталогов ((*/)# синтаксис или его более простая форма **/) и спецификаторы globbing (которые являются кулоном -type f, -mtime, -perm... в find).

**/ часть этого была скопирована ksh93 в 2003, fish в 2005, bash в 2009 и tcsh в 2010 (хотя tcsh также скопированный ***/ часть). И все они не включают его по умолчанию. К сожалению, отметьте это оба bash и fish ** действительно следуйте за символьными ссылками на каталоги (как -L/-follow в find, или *** в zsh или tcsh).

В тех оболочках можно найти pdf файлы на любом уровне подкаталогов, не имея необходимость полагаться find, но отметьте тот протест выше о fish и bash, и только zsh имеет globbing спецификаторы.

Так, например, zsh эквивалентный из:

find . -name '*.pdf' -type f -exec ls -ld {} +

был бы:

ls -ld ./**/*.pdf(D.)

В то время как с bash, необходимо было бы сделать что-то как:

shopt -s failglob
shopt -s globstar
files=(./**/*.pdf) &&
  for i do
    [ -f "$i" ] && ! [ -L "$i" ] && set -- "$i" "$@"
    shift
  done && ls -ld "$@"
8
27.01.2020, 20:31

Существует несколько опций. Придерживаясь решения для цикличного выполнения, можно использовать read считать строку за один раз в переменную:

find . -name "*.pdf" | while IFS= read -r x
do
  echo pathname "$x"
done

Можно также использовать xargs -0 и find -print0 обработать любой вид специальных символов:

find . -name "*.pdf" -print0 | xargs -0 -L1 echo pathname
3
27.01.2020, 20:31

Попробуйте это в остроте:

find ./ -name "*.pdf" -exec echo pathname {} \;
3
27.01.2020, 20:31
  • 1
    Вы пробуете мое решение? Вы знаете значение {} в find команда? –  cuonglm 13.06.2013, 22:10
  • 2
    @terdon, точка Gnouc - это find будет заботиться о цикличном выполнении (использующий циклы в оболочке, обычно плохая практика), и назовет любую команду, которую Вы хотите, чтобы это выполнило, сюда echo как в вопросе OP. Предоставленный, echo плохой выбор команд, но это - команда, которую использовал OP. –  Stéphane Chazelas 13.06.2013, 22:12
  • 3
    @StephaneChazelas, который я ответил на вопрос, чтобы спросить, как безопасно снабдить имя файла пробелами в переменной удара. Я предполагаю, что OP делает что-то более сложное затем echo pathname $foo. Это решение идентично тому, что делает OP, и его вывод все еще не может быть сохранен в переменной. –  terdon♦ 13.06.2013, 22:18
  • 4
    @Gnouc я получаю то, с чем Вы означаете делать -exec но Вы все еще не можете использовать это для сохранения findвывод в переменной. Предоставил, хочет ли весь OP, должен распечатать строку pathname перед фактическим путем затем это - все, в чем Вы нуждаетесь. Но не, если намерение более сложно, чем какой -exec может обработать. И да, я знаю что {} :). –  terdon♦ 13.06.2013, 22:22

После поиска решения для оболочки FreeBSD по умолчанию (/bin/sh), В конце концов я придумал свое собственное решение. Это включает в себя использование sed для уничтожения / создания пробелов. Это также позволит вам изменять переменные в вашем цикле и использовать их внешне, в отличие от использования цикла while (в то время как петли порождают подоболочки в /bin/sh).

REMOTE_FOLDER=/media/images
FILE_LIST=$(find $REMOTE_FOLDER | sed 's/ /\/\/\/\//g')
for FILE in $FILE_LIST; do
    FILE=$(echo $FILE | sed 's/\/\/\/\// /g')
    # do whatever you need to do with $FILE
done
2
27.01.2020, 20:31

Теги

Похожие вопросы