У меня есть сценарий оболочки bash, который просматривает все дочерние каталоги (но не файлы) определенного каталога. Проблема в том, что некоторые имена каталогов содержат пробелы.
Вот содержимое моего тестового каталога:
$ls -F test
Baltimore/ Cherry Hill/ Edison/ New York City/ Philadelphia/ cities.txt
И код, который перебирает каталоги:
for f in `find test/* -type d`; do
echo $f
done
Вот результат:
тест / Балтимор Испытание / Cherry возвышенность Тест / Edison Тест / New Йорк город тест / Филадельфия
Черри-Хилл и Нью-Йорк рассматриваются как 2 или 3 отдельные записи.
Я пробовал цитировать имена файлов, например:
for f in `find test/* -type d | sed -e 's/^/\"/' | sed -e 's/$/\"/'`; do
echo $f
done
но безрезультатно.
Должен быть простой способ сделать это.
Ответы ниже великолепны. Но чтобы усложнить задачу - я не всегда хочу использовать каталоги, перечисленные в моем тестовом каталоге. Иногда я хочу вместо этого передать имена каталогов в качестве параметров командной строки.
Я воспользовался предложением Чарльза о настройке IFS и пришел к следующему:
dirlist="${@}"
(
[[ -z "$dirlist" ]] && dirlist=`find test -mindepth 1 -type d` && IFS=$'\n'
for d in $dirlist; do
echo $d
done
)
и это прекрасно работает, если в аргументах командной строки нет пробелов (даже если эти аргументы заключены в кавычки). Например, вызов сценария следующим образом: test.sh "Cherry Hill" "New York City"
дает следующий результат:
вишня возвышенность новый Йорк город
источник
list="$@"
полностью отбрасывает исходное значение списком, сворачивая его в строку. Пожалуйста, следуйте практике , изложенной в моем ответе, в точности так, как дано - такое задание нигде в нем не поощряется; если вы хотите передать программе список аргументов командной строки, вы должны собрать их в массив и напрямую расширить этот массив.Ответы:
Во-первых, не делайте этого так. Лучший подход -
find -exec
правильно использовать :Другой безопасный подход - использовать список с завершающим NUL, хотя для этого требуется ваша поддержка find
-print0
:Вы также можете заполнить массив из find и передать этот массив позже:
Если ваша находка не поддерживает
-print0
, ваш результат будет небезопасным - приведенный ниже файл не будет вести себя должным образом, если существуют файлы, содержащие символы новой строки в своих именах (что, да, является законным):Если кто-то не собирается использовать один из вышеперечисленных, третий подход (менее эффективный с точки зрения использования времени и памяти, так как он считывает весь вывод подпроцесса перед выполнением разделения слов) заключается в использовании
IFS
переменной, которая не выполняет не содержат пробела. Отключите globbing (set -f
), чтобы предотвратить раскрытие строк, содержащих символы glob, такие как[]
,*
или?
:Наконец, для случая параметра командной строки вы должны использовать массивы, если ваша оболочка поддерживает их (т.е. это ksh, bash или zsh):
сохранит разделение. Обратите внимание, что цитирование (и использование
$@
вместо$*
) важно. Массивы можно заполнять и другими способами, например выражениями глобуса:источник
Однако не работает, если имя файла содержит символы новой строки. Вышеупомянутое - единственное известное мне решение, когда вы действительно хотите, чтобы имя каталога было в переменной. Если вы просто хотите выполнить какую-то команду, используйте xargs.
источник
find . -type d | while read file; do ls "$file"; done
Вот простое решение, которое обрабатывает табуляции и / или пробелы в имени файла. Если вам нужно иметь дело с другими странными символами в имени файла, такими как новые строки, выберите другой ответ.
Каталог тестов
Код для входа в каталоги
Имя файла должно быть заключено в кавычки (
"$f"
), если используется в качестве аргумента. Без кавычек пробелы действуют как разделители аргументов, и вызываемой команде передается несколько аргументов.И вывод:
источник
alias duc='ls -d * | while read D; do du -sh "$D"; done;'
alias duc='du -sh *(/)'
-print0
иIFS='' read -r -d '' f
.Это чрезвычайно сложно в стандартном Unix, и большинство решений не справляются с переводом строки или каким-либо другим символом. Однако, если вы используете набор инструментов GNU, вы можете использовать эту
find
опцию-print0
и использоватьxargs
с соответствующей опцией-0
(минус-ноль). Есть два символа, которые не могут появиться в простом имени файла; это косая черта и NUL '\ 0'. Очевидно, что в именах путей появляется косая черта, поэтому решение GNU по использованию NUL '\ 0' для обозначения конца имени является гениальным и надежным.источник
Почему бы просто не поставить
перед командой for? Это изменяет разделитель полей с <Space> <Tab> <Newline> на <Newline>
источник
источник
-d $'\0'
точно так же, как-d ''
- поскольку bash использует строки с завершающим NUL, первый символ пустой строки - это NUL, и по той же причине NUL вообще не могут быть представлены внутри строк C.я использую
Разве этого не достаточно?
Идея взята из http://www.cyberciti.biz/tips/handling-filenames-with-spaces-in-bash.html
источник
$(echo ...)
), неправильно обрабатывает имена файлов с глобальными выражениями, неправильно обрабатывает имена файлов, содержащие$'\b'
символы или $ '\ n', и, кроме того, преобразует несколько пробелов пробелов в одиночные пробельные символы на сторона вывода из-за неправильного цитирования.Не храните списки в виде строк; храните их как массивы, чтобы избежать путаницы с разделителями. Вот пример сценария, который будет работать либо со всеми подкаталогами test, либо со списком, указанным в его командной строке:
Теперь давайте попробуем это в тестовом каталоге с добавленной кривой или двумя:
источник
"$@"
массив, добавляя к нему с помощьюset -- "$@" "$f"
.Вы можете временно использовать IFS (внутренний разделитель полей), используя:
источник
ps, если речь идет только о пространстве во вводе, то некоторые двойные кавычки у меня работали гладко ...
источник
Чтобы добавить к тому, что сказал Джонатан : используйте
-print0
опцию дляfind
в сочетании соxargs
следующим:Это выполнит команду
command
с правильными аргументами; каталоги с пробелами в них будут правильно заключены в кавычки (т.е. они будут переданы как один аргумент).источник
Приведенный выше код преобразует файлы .mov в .avi. Файлы .mov находятся в разных папках, и в именах папок также есть пробелы . Мой сценарий выше преобразует файлы .mov в файл .avi в той же самой папке. Не знаю, поможет ли это вам, народы.
Случай:
Ура!
источник
echo "$name" | ...
не работает, еслиname
есть-n
, и то, как он ведет себя с именами с обратной косой чертой и escape-последовательностями, зависит от вашей реализации - POSIX делает поведениеecho
в этом случае явно неопределенным (тогда как POSIX с расширенным XSI расширяет стандартные обратные косые черты с escape-последовательностями и системы GNU, включая bash, безPOSIXLY_CORRECT=1
нарушения стандарта POSIX путем реализации-e
(в то время как спецификация требуетecho -e
печати-e
на выходе).printf '%s\n' "$name" | ...
безопаснее.Также приходилось иметь дело с пробелами в именах путей. В конце концов я использовал рекурсию и
for item in /path/*
:источник
function
ключевое слово - оно делает ваш код несовместимым с POSIX sh, но не имеет другой полезной цели. Вы можете просто определить функцию с помощьюrecursedir() {
, добавив две скобки и удалив ключевое слово function, и это будет совместимо со всеми POSIX-совместимыми оболочками.Преобразуйте список файлов в массив Bash. Здесь используется подход Мэтта МакКлюра для возврата массива из функции Bash: http://notes-matthewlmcclure.blogspot.com/2009/12/return-array-from-bash-function-v-2.html Результатом является способ для преобразования любого многострочного ввода в массив Bash.
Этот подход работает, даже когда присутствуют плохие символы, и является общим способом преобразования любого ввода в массив Bash. Недостатком является то, что если ввод длинный, вы можете превысить ограничения на размер командной строки Bash или использовать большой объем памяти.
Подходы, в которых цикл, который в конечном итоге работает со списком, также имеет переданный по конвейеру список, имеют недостаток, заключающийся в том, что чтение stdin непросто (например, запрос у пользователя ввода), а цикл - это новый процесс, поэтому вам может быть интересно, почему переменные , установленный внутри цикла, недоступны после завершения цикла.
Мне также не нравится установка IFS, это может испортить другой код.
источник
IFS='' read
, в той же строке, параметр IFS присутствует только для команды чтения и не экранирует ее. Нет причин не любить такую настройку IFS.Что ж, я вижу слишком много сложных ответов. Я не хочу передавать вывод утилиты find или писать цикл, потому что для этого у find есть опция "exec".
Моя проблема заключалась в том, что я хотел переместить все файлы с расширением dbf в текущую папку, а некоторые из них содержали пробелы.
Я взялся за это так:
Для меня это выглядит очень просто
источник
только что выяснил, что между моим и вашим вопросом есть некоторые сходства . Например, если вы хотите передавать аргументы в команды
распечатать их по порядку
обратите внимание, что $ @ заключен в двойные кавычки, некоторые примечания здесь
источник
Мне нужна была такая же концепция для последовательного сжатия нескольких каталогов или файлов из определенной папки. Я решил использовать awk для анализа списка из ls и избежать проблемы с пустым пространством в имени.
что ты думаешь?
источник
источник
Для меня это работает, и это довольно "чисто":
источник
Просто возникла проблема простого варианта ... Конвертировать файлы из набранного .flv в .mp3 (зевать).
рекурсивно найти все пользовательские флеш-файлы Macintosh и превратить их в аудио (копировать, без перекодирования) ... это похоже на то, что указано выше, отмечая, что чтение вместо просто «для файла в
» исчезнет.
источник
read
После того, какin
еще одно слово в списке вы Перебор. То, что вы опубликовали, представляет собой немного поврежденную версию того, что было у автора вопроса, что не работает. Возможно, вы намеревались опубликовать что-то другое, но, вероятно, здесь есть другие ответы.