Подтвердить что ты не робот

Изменение строки замены в xargs

Когда я использую xargs, иногда мне не нужно явно использовать заменяющую строку:

find . -name "*.txt" | xargs rm -rf

В других случаях я хочу указать заменяющую строку, чтобы сделать что-то вроде:

find . -name "*.txt" | xargs -I '{}' mv '{}' /foo/'{}'.bar

Предыдущая команда переместила бы все текстовые файлы под текущим каталогом в /foo и добавит расширение bar ко всем файлам.

Если вместо добавления некоторого текста в строку replace мне нужно изменить эту строку, чтобы я мог вставить текст между именем и расширением файлов, как я мог это сделать? Например, скажем, я хочу сделать то же, что и в предыдущем примере, но файлы следует переименовать/переместить из <name>.txt в /foo/<name>.bar.txt (вместо /foo/<name>.txt.bar).

ОБНОВЛЕНИЕ: мне удается найти решение:

find . -name "*.txt" | xargs -I{} \
    sh -c 'base=$(basename $1) ; name=${base%.*} ; ext=${base##*.} ; \
           mv "$1" "foo/${name}.bar.${ext}"' -- {}

Но мне интересно, есть ли более короткое/лучшее решение.

4b9b3361

Ответ 1

В подобных случаях цикл while будет более читаемым:

find . -name "*.txt" | while IFS= read -r pathname; do
    base=$(basename "$pathname"); name=${base%.*}; ext=${base##*.}
    mv "$pathname" "foo/${name}.bar.${ext}"
done

Обратите внимание, что вы можете найти файлы с тем же именем в разных подкаталогах. Вы в порядке с дубликатами, написанными над текстом mv?

Ответ 2

Следующая команда создает команду перемещения с помощью xargs, заменяет второе вхождение '.' с '.bar.', затем выполняет команды с bash, работая на Mac OSX.

ls *.txt | xargs -I {} echo mv {} foo/{} | sed 's/\./.bar./2' | bash

Ответ 3

Если у вас установлен GNU Parallel http://www.gnu.org/software/parallel/, вы можете сделать это:

find . -name "*.txt" | parallel 'ext="{/}" ; mv -- {} foo/{/.}.bar.${ext##*.}'

Вы можете установить GNU Parallel просто:

wget http://git.savannah.gnu.org/cgit/parallel.git/plain/src/parallel
chmod 755 parallel
cp parallel sem

Смотрите видеоролики для GNU. Параллельно узнайте больше: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Ответ 4

Это можно сделать за один проход (проверено в GNU), избегая использования временных назначений переменных

find . -name "*.txt" | xargs -I{} sh -c 'mv "$1" "foo/$(basename ${1%.*}).new.${1##*.}"' -- {}

Ответ 5

Если вам разрешено использовать нечто, отличное от bash/sh, и это просто для фантазии "mv"... вы можете попробовать почтенный "rename.pl" script. Я использую его в Linux и cygwin на окнах все время.

http://people.sc.fsu.edu/~jburkardt/pl_src/rename/rename.html

rename.pl 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' list_of_files_or_glob

Вы также можете использовать параметр "-p" для rename.pl, чтобы он рассказывал вам, что он сделал, не выполнив этого.

Я просто попробовал следующее в моей c:/bin (cygwin/windows environment). Я использовал "-p", поэтому он выплюнул, что бы он сделал. Этот пример просто разбивает базу и расширение и добавляет строку между ними.

perl c:/bin/rename.pl -p 's/^(.*?)\.(.*)$/\1-new_stuff_here.\2/' *.bat

rename "here.bat" => "here-new_stuff_here.bat"
rename "htmldecode.bat" => "htmldecode-new_stuff_here.bat"
rename "htmlencode.bat" => "htmlencode-new_stuff_here.bat"
rename "sdiff.bat" => "sdiff-new_stuff_here.bat"
rename "widvars.bat" => "widvars-new_stuff_here.bat"

Ответ 6

Вдохновленный ответом от @justaname выше, эта команда, которая включает в себя однострочный Perl, сделает это:

find ./ -name \*.txt | perl -p -e 's/^(.*\/(.*)\.txt)$/mv $1 .\/foo\/$2.bar.txt/' | bash