Как сделать рекурсивный "для l oop" - PullRequest
3 голосов
/ 23 января 2020

У меня есть команда, которая, кажется, успешно заменяет двойные кавычки (") одинарными кавычками (') для имен файлов в текущем каталоге. Однако мне нужно сделать это рекурсивно (т. Е. Все файлы во всех подкаталогах). Вот код, с которым я работаю:

for f in *; do
    if [[ -f "$f" ]]; then
        new_name=$(echo "$f" | sed "s/\"/'/g")
        mv "$f" "$new_name"
    fi
done

Буду признателен за любой совет.

1 Ответ

4 голосов
/ 23 января 2020

Лучшая практика (и гораздо более эффективное решение, когда вы имеете дело с большим и глубоко вложенным деревом каталогов) - использовать для этого find, а не for l oop в bash при все. Использование Find входит в компромиссы между -print0, -exec и -execdir; здесь я использую последний из них:

#!/usr/bin/env bash

# define the function we're going to export for access from a subprocess
do_replace() {
  local name new_name old_quotes='"' new_quotes="'"
  for name do  # loops by default over "$@", the argument list given to the function
    new_name=${name//$old_quotes/$new_quotes}
    mv -- "$name" "$new_name"
  done
}
export -f do_replace # actually export that function

# tell find to start a new copy of bash that can run the function we exported
# in each directory that contains one or more file or directory names with quotes.
# Using ``-execdir ... {} ';'`` to work around a MacOS bug
# ...use ``-execdir ... {} +`` instead with GNU find for better performance.
find . -depth -name '*"*' -execdir bash -c 'do_replace "$@"' _ {} ';'

Таким образом, есть новая копия bash для каждого каталога, поэтому вы не работаете с именами с / s в них; это позволяет избежать некоторых дыр в безопасности, которые могут возникнуть, если вы переименуете файлы в каталогах, в которые может записать другой пользователь.


При этом вещь easy (в bash 4.0 или позже) должен включить globstar, после чего ** будет повторяться:

#!/usr/bin/env bash
# WARNING: This calculates the whole glob before it runs any renames; this can be very
# inefficient in a large directory tree.

case $BASH_VERSION in ''|[123].*) echo "ERROR: Bash 4.0+ required" >&2; exit 1;; esac
shopt -s globstar
for f in **; do
  : "put your loop's content here as usual"
done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...