Behöver du kombinera flera Linux-kommandon, men ett av dem kan inte hantera indata via pipes? Verktyget `xargs` kan hämta resultatet från ett kommando och använda det som argument till ett annat kommando.
I grund och botten har varje standard Linux-program tre dataflöden kopplade till sig: standardinflödet (stdin), standardutflödet (stdout) och standardfelflödet (stderr).
Dessa flöden använder text. Vi skickar text som input (stdin) till ett kommando, och svaret (stdout) visas som text i terminalen. Felmeddelanden visas också i terminalen som text (stderr).
En kraftfull egenskap hos Linux och Unix-liknande operativsystem är möjligheten att omdirigera stdout från ett kommando till stdin för ett annat. Det första kommandot märker inte att dess output inte går till terminalen, och det andra kommandot vet inte att dess input inte kommer från tangentbordet.
Även om alla Linux-kommandon har dessa tre standardflöden, kan inte alla ta emot ett annat kommandos stdout som input. Det betyder att man inte kan ”pipa” information till dem.
`xargs` är ett kommando för att skapa exekveringskedjor med standarddataflöden. Med `xargs` kan vi få kommandon som `echo`, `rm` och `mkdir` att acceptera standardinput som argument.
`xargs`-kommandot
`xargs` kan ta emot pipad input. Den kan även ta input från en fil. `xargs` använder denna input som parametrar till de kommandon som den ska arbeta med. Om vi inte anger ett kommando till `xargs`, använder det `echo` som standard.
Vi kan illustrera hur `xargs` alltid genererar en enda rad med output, även från input med flera rader.
Om vi använder `-1`-flaggan (lista en fil per rad) med `ls`, får vi en kolumn med filnamn.
ls -1 ./*.sh
Detta listar alla shell-skriptfiler i aktuell katalog.
Vi får en kolumn, som förväntat. Om vi pipar detta genom `xargs`, vad får vi då?
ls -1 ./*.sh | xargs
Output skrivs till terminalen som en lång textsträng.
Det är denna förmåga som gör att vi kan mata in parametrar till andra kommandon med hjälp av `xargs`.
Använda `xargs` med `wc`
Vi kan enkelt använda `xargs` för att få `wc` att räkna ord, tecken och rader i flera filer.
ls *.page | xargs wc
Det här är vad som händer:
`ls` listar alla filer med filändelsen `.page` och skickar listan till `xargs`.
`xargs` skickar filnamnen till `wc`.
`wc` behandlar filnamnen som om de hade angivits direkt som kommandoradsparametrar.
Statistik för varje fil visas, samt en totalsumma.
Använda `xargs` med bekräftelse
Vi kan använda `-p`-flaggan (interaktivt) för att få `xargs` att bekräfta att vi vill fortsätta.
Om vi skickar en rad med filnamn via `xargs`, kommer kommandot `touch` att skapa filerna åt oss.
echo 'one two three' | xargs -p touch
Kommandot som ska köras visas, och `xargs` väntar på att vi ska svara med ”y” eller ”Y”, eller ”n” eller ”N” och trycka på Enter.
Om du bara trycker på Enter behandlas det som ”n”. Kommandot körs bara om du skriver ”y” eller ”Y”.
Vi tryckte på ”y” och Enter. Vi kan kontrollera att filerna har skapats med `ls`.
ls one two three
Använda `xargs` med flera kommandon
Vi kan använda flera kommandon med `xargs` genom att använda `-I`-flaggan (initialt argument).
Denna flagga definierar en ”ersättningssträng”. Där ersättningssträngens token visas i kommandoraden, kommer de värden som ges till `xargs` att infogas.
Låt oss använda kommandot `tree` för att titta på underkatalogerna i den aktuella katalogen. Flaggan `-d` (katalog) gör att `tree` ignorerar filer och bara visar kataloger.
tree -d
Det finns en underkatalog som heter ”bilder”.
I en fil som heter `directories.txt` har vi namnen på några kataloger som vi vill skapa. Vi kan titta på dess innehåll med hjälp av `cat`.
cat directories.txt
Vi kommer att använda detta som input till `xargs`. Kommandot som vi kommer att använda är detta:
cat directories.txt | xargs -I % sh -c 'echo %; mkdir %'
Detta bryts ner så här:
`cat directories.txt |`: Innehållet i filen `directories.txt` (alla nya katalognamn) skickas till `xargs`.
`xargs -I %`: Definierar en ”ersättningssträng” med token `%`.
`sh -c`: Startar ett nytt subshell. `-c` (command) säger till skalet att läsa kommandon från kommandoraden.
`’echo %; mkdir %’`: Varje `%`-token kommer att ersättas med katalognamnen som `xargs` skickar. Kommandot `echo` skriver ut katalognamnet; kommandot `mkdir` skapar katalogen.
Katalogen listas en efter en.
Vi kan använda `tree` igen för att verifiera att katalogerna har skapats.
tree -d
Kopiera filer till flera platser
Vi kan använda `xargs` för att kopiera filer till flera platser med ett enda kommando.
Vi skickar namnen på två kataloger till `xargs` som input. Vi kommer att be `xargs` att skicka endast en av dessa parametrar åt gången till kommandot som det arbetar med.
I det här fallet är kommandot `cp`. Effekten blir att `cp` anropas två gånger, varje gång med en av de två katalogerna som kommandoradsparameter. `xargs`-parametern för att göra detta är `-n` (max antal). Vi kommer att sätta detta till ett.
Vi använder även flaggan `-v` (verbose) med `cp` så att det visar vad som händer.
echo ~/Backups/ ~/Documents/page-files/ | xargs -n 1 cp -v ./*.page
Filerna kopieras till de två katalogerna, en katalog i taget. `cp` rapporterar varje filkopieringsoperation så att vi kan se att de utförs.
Ta bort filer i kapslade kataloger
Om filnamn har mellanslag och udda tecken – som nyradstecken – kan `xargs` inte tolka dem korrekt. Vi kan lösa detta problem genom att använda flaggan `-0` (nollterminator). Detta talar om för `xargs` att använda nulltecknet som sista avgränsare för filnamn.
Vi använder `find` i det här exemplet. `find` har sitt eget alternativ för att hantera mellanslag och udda tecken i filnamn. Det är flaggan `-print0` (fullständigt namn, nolltecken).
find . -name "*.png" -type f -print0 | xargs -0 rm -v -rf "{}"
Detta bryts ner så här:
`find . -name ”*.png”`: `find` söker från aktuell katalog `.` efter objekt som matchar `*.png` och som är filer (type -f).
`-print0`: Namnen avslutas med nulltecken och mellanslag och udda tecken hanteras.
`xargs -0`: `xargs` kommer också att betrakta filnamn som nollterminerade, och mellanslag och udda tecken orsakar inga problem.
`rm -v -rf ”{}”`: `rm` kommer att vara verbose och rapportera vad som händer (-v). Det kommer att vara rekursivt (-r) och leta igenom kapslade kataloger och ta bort filer utan att fråga (-f). `{}` ersätts av varje filnamn.
Alla underkataloger söks igenom och filerna som matchar sökmönstret raderas.
Ta bort kapslade kataloger
Låt oss säga att vi vill ta bort en uppsättning kapslade underkataloger. `tree` kan visa dem.
tree -d
find . -name "level_one" -type d printo | xargs -0 rm -v -rf "{}"
Detta kommando använder `find` för att söka rekursivt i den aktuella katalogen. Sökmålet är en katalog som heter ”level_one”. Katalognamnen skickas via `xargs` till `rm`.
De enda viktiga skillnaderna mellan detta kommando och det föregående är att söktermen är namnet på den översta katalogen, och `-type d` talar om för `find` att söka efter kataloger, inte filer.
Namnet på varje katalog skrivs ut när den tas bort. Vi kan kolla med `tree`:
tree -d
Alla kapslade underkataloger tas bort.
Ta bort alla filer, utom en filtyp
Vi kan använda `find`, `xargs` och `rm` för att ta bort alla filer utom en typ som vi vill behålla. Det är lite kontraintuitivt, men vi anger namnet på den filtyp vi vill behålla, inte namnen på de som vi vill ta bort.
Flaggan `-not` talar om för `find` att returnera namn på de filer som inte matchar sökmönstret. Vi använder flaggan `-I` (initialt argument) med `xargs` igen. Den här gången är ersättningssträngstoken som vi definierar `{}`. Detta kommer att bete sig på samma sätt som den ersättningssträngstoken som vi genererade tidigare, som råkade vara `%`.
find . -type f -not -name "*.sh" -print0 | xargs -0 -I {} rm -v {}
Vi kan kolla med `ls`. De enda filerna som finns kvar i katalogen är de som matchar sökmönstret `*.sh`.
ls -l
Skapa en arkivfil med `xargs`
Vi kan använda `find` för att söka efter filer och skicka dem via `xargs` till `tar`, för att skapa en arkivfil.
Vi söker i den aktuella katalogen. Sökmönstret är `*.page` så vi letar efter `.page`-filer.
find ./ -name "*.page" -type f -print0 | xargs -0 tar -cvzf page_files.tar.gz
Filerna listas som förväntat när arkivfilen skapas.
Dataförmedlaren
Ibland behöver man lite byggnadsställningar för att koppla ihop saker. `xargs` fyller luckan mellan kommandon som kan mata ut information och de kommandon som inte är byggda för att ta emot den.
Både `xargs` och `find` har ett stort antal alternativ. Vi rekommenderar att du tittar på deras ”man”-sidor för att lära dig mer.