Как я могу выйти из пробела в списке цикла удара?

117
задан John Bachir 15 March 2012 в 16:51
поделиться

9 ответов

Во-первых, не делайте этого тот путь. Лучший подход должен использовать find -exec правильно:

# this is safe
find test -type d -exec echo '{}' +

другой безопасный подход должен использовать NUL-завершенный список, хотя это требует что Ваша поддержка находки -print0:

# this is safe
while IFS= read -r -d '' n; do
  printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d -print0)

можно также заполнить массив от находки и передачу, которые выстраивают позже:

# this is safe
declare -a myarray
while IFS= read -r -d '' n; do
  myarray+=( "$n" )
done < <(find test -mindepth 1 -type d -print0)
printf '%q\n' "${myarray[@]}" # printf is an example; use it however you want

, Если Ваша находка не поддерживает -print0, Ваш результат тогда небезопасен - ниже, не будет вести себя, как желаемый, если файлы будут существовать содержащий новые строки на их имена (который, да, законно):

# this is unsafe
while IFS= read -r n; do
  printf '%q\n' "$n"
done < <(find test -mindepth 1 -type d)

, Если Вы не собираетесь использовать одно из вышеупомянутого, третий подход (менее эффективный и с точки зрения времени и с точки зрения использования памяти, поскольку это читает весь вывод подпроцесса прежде, чем сделать разделение слова) должен использовать IFS переменная, которая не содержит пробел. Выключите globbing (set -f) для предотвращения строк, содержащих символы шарика такой как [1 112], * или ? от того, чтобы быть расширенным:

# this is unsafe (but less unsafe than it would be without the following precautions)
(
 IFS= 

Наконец, для случая параметра командной строки, необходимо использовать массивы, если оболочка поддерживает их (т.е. это - ksh, удар или zsh):

# this is safe
for d in "$@"; do
  printf '%s\n' "$d"
done

поддержит разделение. Обратите внимание, что заключение в кавычки (и использование [1 115], а не $*) важно. Массивы могут быть заполнены другими способами также, такими как выражения шарика:

# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
  printf '%s\n' "$d"
done
\n' # split only on newlines set -f # disable globbing for n in $(find test -mindepth 1 -type d); do printf '%q\n' "$n" done )

Наконец, для случая параметра командной строки, необходимо использовать массивы, если оболочка поддерживает их (т.е. это - ksh, удар или zsh):

# this is safe
for d in "$@"; do
  printf '%s\n' "$d"
done

поддержит разделение. Обратите внимание, что заключение в кавычки (и использование [1 115], а не $*) важно. Массивы могут быть заполнены другими способами также, такими как выражения шарика:

# this is safe
entries=( test/* )
for d in "${entries[@]}"; do
  printf '%s\n' "$d"
done
103
ответ дан Charles Duffy 24 November 2019 в 02:08
поделиться

Добавить к какой сказанный Jonathan: используйте -print0 опция для find в сочетании с xargs следующим образом:

find test/* -type d -print0 | xargs -0 command

, Который выполнит команду command с надлежащими аргументами; каталоги с пробелами в них будут правильно заключены в кавычки (т.е. они будут переданы в как один аргумент).

2
ответ дан Community 24 November 2019 в 02:08
поделиться

Это чрезвычайно хитро в стандартном Unix, и большинство решений ссорится с новыми строками или некоторым другим символом. Однако при использовании комплекта инструментальных средств GNU тогда можно использовать find опция -print0 и использование xargs с соответствующей опцией -0 (минус нуль). Существует два символа, которые не могут появиться в простом имени файла; те - наклонная черта и NUL '\0'. Очевидно, наклонная черта появляется в путях, таким образом, решение для GNU использования NUL '\0' для маркировки конца имени является оригинальным и надежным.

7
ответ дан Jonathan Leffler 24 November 2019 в 02:08
поделиться
find . -type d | while read file; do echo $file; done

Однако не работает, если имя файла содержит новые строки. Вышеупомянутое является единственным решением, которое я знаю того, когда Вы на самом деле хотите иметь имя каталога в переменной. Если Вы просто хотите выполнить некоторую команду, используйте xargs.

find . -type d -print0 | xargs -0 echo 'The directory is: '
26
ответ дан Johannes Schaub - litb 24 November 2019 в 02:08
поделиться

Не хранить списки в виде строк; храните их как массивы, чтобы избежать путаницы. Вот пример сценария, который будет работать либо со всеми подкаталогами test, либо со списком, предоставленным в его командной строке:

#!/bin/bash
if [ $# -eq 0 ]; then
        # if no args supplies, build a list of subdirs of test/
        dirlist=() # start with empty list
        for f in test/*; do # for each item in test/ ...
                if [ -d "$f" ]; then # if it's a subdir...
                        dirlist=("${dirlist[@]}" "$f") # add it to the list
                fi
        done
else
        # if args were supplied, copy the list of args into dirlist
        dirlist=("$@")
fi
# now loop through dirlist, operating on each one
for dir in "${dirlist[@]}"; do
        printf "Directory: %s\n" "$dir"
done

Теперь давайте попробуем это на тестовом каталоге с добавленной кривой или двумя:

$ ls -F test
Baltimore/
Cherry Hill/
Edison/
New York City/
Philadelphia/
this is a dirname with quotes, lfs, escapes: "\''?'?\e\n\d/
this is a file, not a directory
$ ./test.sh 
Directory: test/Baltimore
Directory: test/Cherry Hill
Directory: test/Edison
Directory: test/New York City
Directory: test/Philadelphia
Directory: test/this is a dirname with quotes, lfs, escapes: "\''
'
\e\n\d
$ ./test.sh "Cherry Hill" "New York City"
Directory: Cherry Hill
Directory: New York City
4
ответ дан 24 November 2019 в 02:08
поделиться

just found out there are some similarities between my question and yours. Aparrently if you want to pass arguments into commands

test.sh "Cherry Hill" "New York City"

to print them out in order

for SOME_ARG in "$@"
do
    echo "$SOME_ARG";
done;

notice the $@ is surrounded by double quotes, some notes here

0
ответ дан 24 November 2019 в 02:08
поделиться

Также приходилось иметь дело с пробелами в именах путей. В конце концов я использовал рекурсию и для элемента в / path / * :

function recursedir {
    local item
    for item in "${1%/}"/*
    do
        if [ -d "$item" ]
        then
            recursedir "$item"
        else
            command
        fi
    done
}
1
ответ дан 24 November 2019 в 02:08
поделиться

Вот простое решение, которое обрабатывает табуляции и / или пробелы в имени файла. Если вам приходится иметь дело с другими странными символами в имени файла, такими как новые строки, выберите другой ответ.

Тестовый каталог

ls -F test
Baltimore/  Cherry Hill/  Edison/  New York City/  Philadelphia/  cities.txt

Код для входа в каталоги

find test -type d | while read f ; do
  echo "$f"
done

Имя файла должно быть заключено в кавычки ( "$ f "), если используется в качестве аргумента. Без кавычек пробелы действуют как разделители аргументов, и вызываемой команде передается несколько аргументов.

И вывод:

test/Baltimore
test/Cherry Hill
test/Edison
test/New York City
test/Philadelphia
22
ответ дан 24 November 2019 в 02:08
поделиться

Просто возникла проблема простого варианта ... Преобразование файлов из типизированного .flv в .mp3 (зевок).

for file in read `find . *.flv`; do ffmpeg -i ${file} -acodec copy ${file}.mp3;done

рекурсивно найти все пользовательские флэш-файлы Macintosh и превратить их в аудио (копировать, без перекодирования) ... это похоже на while выше, отмечая, что чтение вместо просто «для файла в » ускользнет.

-4
ответ дан 24 November 2019 в 02:08
поделиться
Другие вопросы по тегам:

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