Лучшая практика (и гораздо более эффективное решение, когда вы имеете дело с большим и глубоко вложенным деревом каталогов) - использовать для этого 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