Unix-конвейер команд переименования

Поскольку bash завершает работу после завершения команды, вы можете вызвать другой экземпляр bashпосле своей команды, чтобы получить интерактивную оболочку:

gnome-terminal  --tab -e "bash -c 'ps -ef; bash'" --tab -e "bash -c 'ls; bash'" --tab -e "bash -c 'top -n 1; bash'"
0
19.12.2019, 23:36
3 ответа

Рассмотрите возможность использования последовательности команд sedдля преобразования начального имени файла в конечное имя файла, а затем выполнения однойmvдля фактического переименования файла:

for i in *%*
do
    printf "mv -v '%s' '%s'\n" "$i" \
      "$( sed -e 's/%20/ /g' \
              -e 's/%28/(/g' \
         <<< "$i" )"
done

Этот список команд на самом деле не изменяет ваши файлы, и это хорошо. Он просто выводит команды mv, которые вы можете использовать, чтобы сделать то, что вы пытаетесь сделать.

Давайте создадим несколько примеров файлов, содержащих %20и %28вхождения:

$ for i in foo bar farkle
> do
>     touch $i"%20".txt; touch $i"%28".txt; touch $i"%20-%28".txt
> done
$ ls -1
bar%20-%28.txt
bar%20.txt
bar%28.txt
farkle%20-%28.txt
farkle%20.txt
farkle%28.txt
foo%20-%28.txt
foo%20.txt
foo%28.txt

Теперь запустим приведенный в начале список команд:

$ for i in *%*
> do
>     printf "mv -v '%s' '%s'\n" "$i" \
>       "$(sed -e 's/%20/ /g' -e 's/%28/(/g' <<< "$i")"
> done
mv -v 'bar%20-%28.txt' 'bar -(.txt'
mv -v 'bar%20.txt' 'bar.txt'
mv -v 'bar%28.txt' 'bar(.txt'
mv -v 'farkle%20-%28.txt' 'farkle -(.txt'
mv -v 'farkle%20.txt' 'farkle.txt'
mv -v 'farkle%28.txt' 'farkle(.txt'
mv -v 'foo%20-%28.txt' 'foo -(.txt'
mv -v 'foo%20.txt' 'foo.txt'
mv -v 'foo%28.txt' 'foo(.txt'

Если эта последовательность команд mvкажется вам правильной, нажмите стрелку вверх -, чтобы запустить команду еще раз, но на этот раз добавьте | shв конце, чтобы фактически выполнить эти команды mvвместо отображения их.

1
28.01.2020, 02:38
find. -type f -name '*%*' -execdir bash -c '
    name=$1
    name=${name//%20/ }    # replace each %20 by a space
    name=${name//%28/(}    # replace each %28 by a (
    mv "$1" "$name"' bash {} \;

Используется findдля поиска любого обычного файла в текущем каталоге или ниже него, имя которого содержит символ %. Для каждого такого файла выполняется короткий сценарий bash, который выполняет две замены, указанные вами в данном имени файла, а затем переименовывает файл.

Было бы легко добавить к этому другие подстановки параметров, чтобы удалить или изменить другие строки в именах файлов.

Возможно, более быстрым способом было бы написать простой цикл, чтобы сделать то же самое, (предполагая оболочку bashдля этого конкретного варианта):

shopt -s nullglob dotglob globstar

for pathname in./**/*%*; do
    # skip pathnames to non-regular (or links to non-regular) files
    [[ ! -f "$pathname" ]] && continue

    name=${pathname##*/}    # strip off directory components
    name=${name//%20/ }     #... so that we don't accidentally
    name=${name//%28/(}     #... modify the directory path

    # the destination is made up by the directory path
    # (original pathname with filename stripped off) 
    # followed by the new name
    mv "$pathname" "${pathname%/*}/$name"
done

Параметр оболочки nullglobзаставляет шаблон ./**/*%*расширяться до ничего если он ничему не соответствует (он обычно остается нераскрытым ), dotglobпозволяет подстановку шаблонов для сопоставления скрытых имен, а globstarпозволяет использовать **для "рекурсивного" сопоставления с подкаталогами.

Адаптация этого второго цикла к чему-то, что использует findдля генерации имен путей, а не шаблона подстановки (, например. для работы с bashвыпусками до выпуска 4, в котором не было**):

find. -type f -name '*%*' -exec bash -c '
    for pathname do
        name=${pathname##*/}    # strip off directory components
        name=${name//%20/ }     #... so that we don't accidentally
        name=${name//%28/(}     #... modify the directory path

        # the destination is made up by the directory path
        # (original pathname with filename stripped off) 
        # followed by the new name
        mv "$pathname" "${pathname%/*}/$name"
    done' bash {} +
0
28.01.2020, 02:38

Похоже, что по своей сути это упражнение в декодировании URL, что является чем-то вроде решенной проблемы.

Сохраните это как, скажем,rename_script.sh:

DECODED="$(awk -niord '{printf RT?$0chr("0x"substr(RT,2)):$0}' RS=%.. <<< "${1}")"
[ ! -f "${1}" ] && echo "Source not found" && exit 1
[ -f "${DECODED}" ] && echo "Target already exists" && exit 1
[ "${1}" != "${DECODED}" ] && mv -i "${1}" "${DECODED}" || echo "Nothing to do"

И призывать таким образом:

find -name "*%*" -print0 | xargs -n1 -0./rename_script.sh

Большой кредит этому ответу :https://stackoverflow.com/a/14000368/2858703

0
28.01.2020, 02:38

Теги

Похожие вопросы