Vervang string wijzigen in xargs

Als ik xargsgebruik, hoef ik soms niet expliciet de vervangende string te gebruiken:

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

In andere gevallen wil ik de vervangende string specificeren om dingen te doen als:

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

De vorige opdracht verplaatste alle tekstbestanden onder de huidige map naar /fooen voegt de extensie bartoe aan alle bestanden.

Als ik in plaats van wat tekst toe te voegen aan de vervangende tekenreeks, die tekenreeks zou willen wijzigen zodat ik wat tekst tussen de naam en de extensie van de bestanden kon invoegen, hoe zou ik dat dan kunnen doen? Laten we bijvoorbeeld zeggen dat ik hetzelfde wil doen als in het vorige voorbeeld, maar de bestanden moeten worden hernoemd/verplaatst van <name>.txtnaar /foo/<name>.bar.txt(in plaats van /foo/<name>.txt.bar).

UPDATE: het lukt me een oplossing te vinden:

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

Maar ik vraag me af of er een kortere/betere oplossing is.


Antwoord 1, autoriteit 100%

Het volgende commando construeert het move commando met xargs, vervangt het tweede voorkomen van ‘.’ met ‘.bar.’, voert dan de commando’s uit met bash, werkend op mac OSX.

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

Antwoord 2, autoriteit 64%

Het is mogelijk om dit in één keer te doen (getest in GNU) zonder het gebruik van tijdelijke variabele toewijzingen

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

Antwoord 3, autoriteit 40%

In dit soort gevallen zou een while-lus beter leesbaar zijn:

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

Houd er rekening mee dat u bestanden met dezelfde naam in verschillende submappen kunt vinden. Vindt u het goed dat duplicaten worden overschreven door mv?


Antwoord 4, autoriteit 23%

Als je GNU Parallel http://www.gnu.org/software/parallel/geïnstalleerd, kunt u dit doen:

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

Bekijk de introductievideo’s voor GNU Parallel voor meer informatie:
https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


Antwoord 5, autoriteit 4%

Als je iets anders dan bash/sh mag gebruiken, EN dit is alleen voor een mooie “mv”… zou je het eerbiedwaardige “rename.pl”-script kunnen proberen. Ik gebruik het de hele tijd op Linux en cygwin op Windows.

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

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

Je kunt ook een “-p” parameter gebruiken om de naam van pl te wijzigen, zodat het je vertelt wat het ZOU GEDAAN HEBBEN, zonder het daadwerkelijk te doen.

Ik heb zojuist het volgende geprobeerd in mijn c:/bin (cygwin/windows-omgeving). Ik gebruikte de “-p”, dus het spuugde uit wat het zou hebben gedaan. Dit voorbeeld splitst alleen de basis en extensie en voegt er een string tussen toe.

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"

Antwoord 6, autoriteit 4%

de bestanden moeten worden hernoemd/verplaatst van <name>.txtnaar /foo/<name>.bar.txt

U kunt het hulpprogramma renamegebruiken, bijvoorbeeld:

rename s/\.txt$/\.txt\.bar/g *.txt

Hint: de vervangingssyntaxis is vergelijkbaar met sedof vim.

Verplaats de bestanden vervolgens naar een doelmap met behulp van mv:

mkdir /some/path
mv *.bar /some/path

Als u bestanden wilt hernoemen naar submappen op basis van een deel van hun naam, controleert u op:

-p/--mkpath/--make-dirsMaak eventuele niet-bestaande mappen in het doelpad.


Testen:

$ touch {1..5}.txt
$ rename --dry-run "s/.txt$/.txt.bar/g" *.txt
'1.txt' would be renamed to '1.txt.bar'
'2.txt' would be renamed to '2.txt.bar'
'3.txt' would be renamed to '3.txt.bar'
'4.txt' would be renamed to '4.txt.bar'
'5.txt' would be renamed to '5.txt.bar'

Antwoord 7, autoriteit 2%

Daaraan toevoegen is het wikipedia-artikelverrassend informatief

bijvoorbeeld:

Shell-truc

Een andere manier om een ​​soortgelijk effect te bereiken is om een ​​shell te gebruiken als het gelanceerde commando en om te gaan met de complexiteit in die shell, bijvoorbeeld:

$ mkdir ~/backups
$ find /path -type f -name '*~' -print0 | xargs -0 bash -c 'for filename; do cp -a "$filename" ~/backups; done' bash

Antwoord 8

Geïnspireerd door een antwoord van @justaname hierboven, zal dit commando, dat de one-liner van Perl bevat, het doen:

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

Other episodes