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 служить разделителем полей - в вашем случае это просто сработало, потому что пути к файлам не содержали этих символов.
  • оператор += не распознается - весь токен 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}??

Другие вопросы по тегам