GNU/Linux >> Znalost Linux >  >> Linux

Jak nahradit řetězec v souboru (souborech)?

Nahrazování řetězců v souborech na základě určitých kritérií vyhledávání je velmi běžný úkol. Jak mohu

  • nahradit řetězec foo s bar ve všech souborech v aktuálním adresáři?
  • provádět totéž rekurzivně pro podadresáře?
  • nahradit pouze v případě, že název souboru odpovídá jinému řetězci?
  • nahradit pouze v případě, že je řetězec nalezen v určitém kontextu?
  • nahradit, pokud je řetězec na určitém čísle řádku?
  • nahradit více řetězců stejnou náhradou
  • nahradit více řetězců různými náhradami

Přijatá odpověď:

1. Nahrazení všech výskytů jednoho řetězce jiným ve všech souborech v aktuálním adresáři:

To jsou případy, kdy znáte že adresář obsahuje pouze běžné soubory a že chcete zpracovat všechny neskryté soubory. Pokud tomu tak není, použijte přístupy v 2.

Vše sed řešení v této odpovědi předpokládají GNU sed . Pokud používáte FreeBSD nebo macOS, nahraďte -i s -i '' . Všimněte si také, že použití -i přepnout s jakoukoli verzí sed má určité důsledky pro zabezpečení souborového systému a nedoporučuje se v žádném skriptu, který plánujete jakýmkoli způsobem distribuovat.

  • Nerekurzivní, soubory pouze v tomto adresáři:

     sed -i -- 's/foo/bar/g' *
     perl -i -pe 's/foo/bar/g' ./* 
    

(perl jeden selže pro názvy souborů končící na | nebo mezera)).

  • Rekurzivní, běžné soubory (včetně skrytých ) v tomto a všech podadresářích

     find . -type f -exec sed -i 's/foo/bar/g' {} +
    

    Pokud používáte zsh:

     sed -i -- 's/foo/bar/g' **/*(D.)
    

    (může selhat, pokud je seznam příliš velký, viz zargs obejít).

    Bash nemůže přímo kontrolovat běžné soubory, je potřeba smyčka (složené závorky se vyhýbají globálnímu nastavení možností):

     ( shopt -s globstar dotglob;
         for file in **; do
             if [[ -f $file ]] && [[ -w $file ]]; then
                 sed -i -- 's/foo/bar/g' "$file"
             fi
         done
     )
    

    Soubory jsou vybrány, když jsou skutečnými soubory (-f) a lze do nich zapisovat (-w).

2. Nahraďte pouze v případě, že název souboru odpovídá jinému řetězci / má specifickou příponu / je určitého typu atd:

  • Nerekurzivní, soubory pouze v tomto adresáři:

    sed -i -- 's/foo/bar/g' *baz*    ## all files whose name contains baz
    sed -i -- 's/foo/bar/g' *.baz    ## files ending in .baz
    
  • Rekurzivní, běžné soubory v tomto a všech podadresářích

    find . -type f -name "*baz*" -exec sed -i 's/foo/bar/g' {} +
    

    Pokud používáte bash (složené závorky se vyhýbají globálnímu nastavení možností):

    ( shopt -s globstar dotglob
        sed -i -- 's/foo/bar/g' **baz*
        sed -i -- 's/foo/bar/g' **.baz
    )
    

    Pokud používáte zsh:

    sed -i -- 's/foo/bar/g' **/*baz*(D.)
    sed -i -- 's/foo/bar/g' **/*.baz(D.)
    

-- slouží ke sdělování sed že v příkazovém řádku nebudou zadávány žádné další příznaky. To je užitečné pro ochranu před názvy souborů začínajícími - .

  • Pokud je soubor určitého typu, například spustitelný (viz man find pro více možností):

    find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
    

zsh :

    sed -i -- 's/foo/bar/g' **/*(D*)

3. Nahraďte pouze v případě, že je řetězec nalezen v určitém kontextu

  • Nahraďte foo s bar pouze pokud existuje baz později na stejném řádku:

     sed -i 's/foo(.*baz)/bar1/' file
    

V sed pomocí ( ) uloží vše, co je v závorkách, a můžete k tomu přistupovat pomocí 1 . Existuje mnoho variant tohoto tématu. Chcete-li se o těchto regulárních výrazech dozvědět více, viz zde.

  • Nahraďte foo s bar pouze pokud foo se nachází ve 3D sloupci (pole) vstupního souboru (za předpokladu, že pole oddělená mezerami):

     gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file
    

(potřebuje gawk 4.1.0 nebo novější).

  • Pro jiné pole stačí použít $N kde N je číslo zájmového pole. Pro jiný oddělovač polí (: v tomto příkladu) použijte:

     gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file
    

Další řešení pomocí perl :

    perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@Fn"' foo 

POZNÁMKA:obě awk a perl řešení ovlivní mezery v souboru (odstraňte úvodní a koncové mezery a převeďte sekvence mezer na jeden znak mezery v řádcích, které se shodují). Pro jiné pole použijte $F[N-1] kde N je požadované číslo pole a pro použití jiného oddělovače polí ($"=":" nastaví oddělovač výstupních polí na : ):

    perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Nahraďte foo s bar pouze na 4. řádku:

     sed -i '4s/foo/bar/g' file
     gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file
     perl -i -pe 's/foo/bar/g if $.==4' file
    

4. Operace vícenásobného nahrazení:nahrazení různými řetězci

  • sed můžete kombinovat příkazy:

     sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
    

Uvědomte si, že na pořadí záleží (sed 's/foo/bar/g; s/bar/baz/g' nahradí foo s baz ).

  • nebo příkazy Perlu

     perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file
    
  • Pokud máte velký počet vzorů, je snazší uložit vzory a jejich náhrady do sed soubor skriptu:

     #! /usr/bin/sed -f
     s/foo/bar/g
     s/baz/zab/g
    
  • Nebo, pokud máte příliš mnoho párů vzorů, než aby to bylo možné, můžete páry vzorů přečíst ze souboru (dva vzory oddělené mezerou, $vzor a $replacement, na řádek):

     while read -r pattern replacement; do   
         sed -i "s/$pattern/$replacement/" file
     done < patterns.txt
    
  • To bude poměrně pomalé pro dlouhé seznamy vzorů a velké datové soubory, takže možná budete chtít vzory přečíst a vytvořit sed místo toho skript od nich. Následující předpokládá <mezera> oddělovač odděluje seznam MATCH<mezera>NAHRADIT páry vyskytující se po jednom na řádek v souboru patterns.txt :

     sed 's| *([^ ]*) *([^ ]*).*|s/1/2/g|' <patterns.txt |
     sed -f- ./editfile >outfile
    

Výše uvedený formát je do značné míry libovolný a například neumožňuje <mezeru> v jednom z MATCH nebo NAHRADIT . Tato metoda je však velmi obecná:v podstatě, pokud můžete vytvořit výstupní proud, který vypadá jako sed skript, pak můžete tento stream získat jako sed skript zadáním sed soubor skriptu jako - stdin.

  • Podobným způsobem můžete kombinovat a spojovat více skriptů:

     SOME_PIPELINE |
     sed -e'#some expression script'  
         -f./script_file -f-          
         -e'#more inline expressions' 
     ./actual_edit_file >./outfile
    

POSIX sed zřetězí všechny skripty do jednoho v pořadí, v jakém se objeví na příkazovém řádku. Žádný z nich nemusí končit n ewline.

  • grep může fungovat stejným způsobem:

     sed -e'#generate a pattern list' <in |
     grep -f- ./grepped_file
    
  • Při práci s pevnými řetězci jako vzory je dobrým zvykem vyhnout se regulárnímu výrazu metaznaky . Můžete to udělat poměrně snadno:

     sed 's/[]$&^*./[]/\&/g
          s| *([^ ]*) *([^ ]*).*|s/1/2/g|
     ' <patterns.txt |
     sed -f- ./editfile >outfile
    

5. Operace vícenásobného nahrazení:nahrazení více vzorů stejným řetězcem

  • Nahraďte libovolné z foo , bar nebo baz pomocí foobar

     sed -Ei 's/foo|bar|baz/foobar/g' file
    
  • nebo

     perl -i -pe 's/foo|bar|baz/foobar/g' file
    

Linux
  1. Jak použít Sed k výměně víceřádkového řetězce?

  2. Jak nahradit řetězec řetězcem obsahujícím lomítko se Sed?

  3. Jak předat řetězec (ne soubor) do Openssl?

  1. Odstraňte výskyty řetězce v textovém souboru

  2. Jak nahradit řetězec ve více souborech v příkazovém řádku linuxu

  3. Jak použít sed k nahrazení proměnné konfiguračního souboru?

  1. Jak nahradit text podobný sedu pythonem?

  2. Jak mohu přidat řetězec na začátek každého souboru ve složce v bash?

  3. nahradit n-tý výskyt řetězce v každém řádku textového souboru