Linux/sh: Как перечислить только файлы в папке (с witespace) и сохранить в переменную в одну строку
Я пытаюсь получить списки файлов в этом формате (с пробелами):
"file1.html" "file 2.php" "file_3.php"
#!/bin/sh
WEB_DIR="/volume1/web"
IFS=$'\n'
for file in $(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f) ; do
printf "\"$file\" "
done
выход:
"/volume1/web/.htaccess" "/volume1/web/file.html" "/volume1/web/a b.php"
и вывод идеально, но... как поместить этот вывод в переменную?
Я сделаю это...
IFS=$'\n'
for file in $(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f) ; do
mystring+=$(printf "\"$file\" ")
done
echo ${mystring}
В выводе у меня есть это:
tmp.sh: line 48: mystring+="/volume1/web/.htaccess" : not found
2 ответа
Замечания:
В приведенном ниже ответе принимается предпосылка вопроса: построить одно строковое значение со списком путей к файлам в двойных кавычках, таких как показано в вопросе (
"/volume1/web/.htaccess" "/volume1/web/file.html" "/volume1/web/a b.php"
)Тем не менее, OP в конечном итоге хотел использовать эту строку как часть другой команды, которая не работает, потому что встроенные двойные кавычки больше не распознаются как разделители строк, которые идентифицируют отдельные аргументы, когда вы ссылаетесь на строковую переменную.
Правильное решение заключается в использовании
find ... -exec ...+
(в этом случае) или, вообще,xargs
передать список имен файлов в качестве операндов (аргументов) другой команде; например, чтобы передать имена файлов в командуfoo
({}
надежно передает все пути к файлам, независимо от того, содержат они пробелы или нет):find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -exec foo -bar {} +
Если список имен файлов не идет в конце целевой командной строки, промежуточный
sh -c
команда необходима:find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -exec sh -c 'foo -bar "$@" -baz' - {} +
Вы пометили свой вопрос bash, но ваша строка shebang нацелена на sh, где расширения Bash для спецификации оболочки POSIX не гарантированы.
В вашем случае (похоже, вы используете dash
, которые действуют как /bin/sh
на Ubuntu):
ANSI C-кавычки, такие как
$'\n'
не доступны -$'\n'
расширяется до буквального$\n
,- Это означает, что любой из этих 3 буквальных символов. -
$
,\
или жеn
служить разделителем полей - в вашем случае это просто сработало, потому что пути к файлам не содержали этих символов.
- Это означает, что любой из этих 3 буквальных символов. -
оператор
+=
не распознается - весь токенmystring+="/volume1/web/.htaccess"
рассматривается как имя команды, которое вызывает ошибку, которую вы видите.
Возможные решения:
Если вы хотите нацелиться на Bash, замените
#!/bin/sh
с#!/bin/bash
,- Обратите внимание, что для вашего
bash
Чтобы код был полностью устойчивым, вы должны отключить его.set +f
) в дополнение к настройке$IFS
, - Ваш код может быть оптимизирован - см. Ниже.
- Обратите внимание, что для вашего
Если нет (если ваш код должен быть переносимым), вы должны найти POSIX-совместимые альтернативы - см. Ниже.
Вот портативное решение (работает с любым POSIX-совместимым sh
):
while IFS= read -r file; do
mystring="$mystring$(printf "\"$file\" ")"
done <<EOF
$(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f)
EOF
echo "$mystring"
Гораздо более эффективный вариант, который использует find ... -exec ... +
производить вывод с (как правило) одним printf
вызов:
IFS= read -r mystring <<EOF
$(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -exec printf '"%s" ' {} +)
EOF
echo "$mystring"
bash
эквивалентно, используя подстановку процесса (<(...)
):
IFS= read -r mystring < \
<(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -exec printf '"%s" ' {} +)
echo "$mystring"
Также обратите внимание, что GNU find
имеет встроенный -printf
действие, которое поддерживает различные строки формата, что делает вызов внешнего printf
утилита ненужная:
IFS= read -r mystring < <(find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -printf '"%p" ')
echo "$mystring"
Я не совсем уверен, почему мы беспокоимся о промежуточном этапе использования переменной:
find "${WEB_DIR}" -mindepth 1 -maxdepth 1 -type f -print0 | xargs -0 /usr/syno/bin/7z \
a "${BACKUP_DIR}/backup_webfiles_${TIMESTAMP}.7z" -xr!thumbs.db -xr!@eaDir -xr!@tmp \
-xr!#recycle -xr!lost+found -xr!.DS_Store -t7z -m0=lzma2 -ms=off -mhe -mmt -mx9 \
-v${SPLIT_VOLUME} -p"${PASSWORD}"
Я не уверен, нужно ли вам сообщать 7z, что файлы поступают из stdin, поэтому вам, возможно, придется добавить дефис (-), где раньше использовался ${only_files}??