Odpowiedzi:
cd /path/to/your/folder
sed -i 's/foo/bar/g' *
Wystąpienia „foo” zostaną zastąpione przez „bar”.
W systemach BSD, takich jak macOS, musisz zapewnić rozszerzenie kopii zapasowej, takie jak -i '.bak'
„ryzyko uszkodzenia lub częściowej zawartości” na stronę podręcznika.
cd /path/to/your/folder
sed -i '.bak' 's/foo/bar/g' *
sed
, -i
wymaga ciąg po nim, który jest dołączany do starych nazw plików. sed -i .bak 's/foo/bar/g' *.xx
Przenosi więc wszystkie .xx
pliki na równoważne .xx.bak
nazwy, a następnie generuje .xx
pliki z podstawieniem foo → bar.
sed -i 's/foo\ with\ spaces/bar/g' *
. Przypuszczam, że dowiedziałeś się po tak długim czasie ... ale w ten sposób pozostanie to dla innych, którzy znajdą ten sam problem.
sed -i -e 's/foo/bar/g' $(find /home/user/base/dir)
Podobne do odpowiedzi Kaspara, ale z flagą g, która zastępuje wszystkie wystąpienia w linii.
find ./ -type f -exec sed -i 's/string1/string2/g' {} \;
W przypadku globalnej nie uwzględniającej wielkości liter:
find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;
LC_ALL=C find ./ -type f -exec sed -i '' -e 's/proc.priv/priv/g' {} \;
(zobacz ten post i ten )
--include=*.whatever
pomocą opcji -r
.git/
. Pamiętaj, aby cd
zejść na niższy poziom.
git status
powraca: error: bad index file sha1 signature.... fatal: index file corrupt
. Co daje?
Odpowiedź @ kev jest dobra, ale wpływa tylko na pliki w bezpośrednim katalogu. Poniższy przykład używa grep do rekurencyjnego wyszukiwania plików. Działa dla mnie za każdym razem.
grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @
Podział poleceń
grep -r : --recursive , rekurencyjnie odczytuje wszystkie pliki w każdym katalogu.
grep -l : --print-with-mecze , drukuje nazwę każdego pliku, który ma dopasowanie, zamiast wypisywać pasujące linie.
grep -i : --ignore-case .
xargs : przekształć STDIN na argumenty, postępuj zgodnie z tą odpowiedzią .
Komenda xargs -i @ ~ zawiera @ ~ : symbol zastępczy dla argumentu, który ma być użyty w określonej pozycji w komendzie ~ , znak @ to symbol zastępczy, który można zastąpić dowolnym łańcuchem.
sed -i : edytuj pliki w miejscu, bez kopii zapasowych.
sed s / regexp / replace / : zastępuj ciąg pasujący wyrażenie regularne z zamianą .
sed s / regexp / replace / g : global , dokonaj podstawienia dla każdego dopasowania zamiast tylko pierwszego.
grep --include={*.php,*.html,*.js} -rnl './' -e "old-word" | xargs -i@ sed -i 's/old-word/new-word/g' @
|
tym, dlaczego -i
w komendzie xargs? Przeczytałem w instrukcji, że jest przestarzała i należy jej użyć -I
. I czy jest @
używany jako ogranicznik początku i końca wzorca?
grep -rli 'old-word' * | xargs -I@ sed -i '' 's/2.0.0/latest/g' @
rli
) i @
znak, na przykład
To działało dla mnie:
find ./ -type f -exec sed -i 's/string1/string2/' {} \;
Howerver, to nie: sed -i 's/string1/string2/g' *
. Może „foo” nie miało być string1, a „bar” nie string2.
find ./ -type f -exec sed -i '' -e 's/string1/string2/' {} \;
Istnieje kilka standardowych odpowiedzi na to pytanie. Zasadniczo można użyć polecenia find, aby rekurencyjnie wyświetlić listę plików, a następnie wykonać operacje za pomocą sed lub perl .
W przypadku większości szybkich zastosowań może okazać się, że polecenie rpl jest znacznie łatwiejsze do zapamiętania. Oto zamiennik (foo -> pasek), rekurencyjnie dla wszystkich plików:
rpl -R foo bar .
Prawdopodobnie będziesz musiał go zainstalować ( apt-get install rpl
lub podobny).
Jednak w przypadku trudniejszych zadań, które wymagają wyrażeń regularnych i podstawiania wstecz lub zmian nazw plików, a także wyszukiwania i zamiany, najbardziej ogólnym i potężnym narzędziem, o którym wiem, jest repren , niewielki skrypt Pythona, dla którego napisałem jakiś czas temu bardziej wymagające zmiany nazw i refaktoryzacji. Powody, dla których możesz to preferować to:
Aby go użyć, pip install repren
. Sprawdź README dla przykładów.
Aby zamienić ciąg w wielu plikach, możesz użyć:
grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'
Na przykład
grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'
Aby zamienić ścieżkę w plikach (unikając znaków specjalnych), możesz użyć następującego polecenia:
sed -i 's@old_path@new_path@g'
Znak @ oznacza, że wszystkie znaki specjalne należy zignorować w następującym ciągu.
Jeśli Twój ciąg zawiera ukośnik (/), możesz zmienić separator na „+”.
find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +
To polecenie uruchomi się rekurencyjnie w bieżącym katalogu.
../domain.com
dodomain.com
Wystąpienia pierwszego wiersza wyrażenia „foo” zostaną zastąpione wyrazem „bar”. I możesz użyć drugiej linii do sprawdzenia.
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c
grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
Jeśli masz listę plików, których możesz użyć
replace "old_string" "new_string" -- file_name1 file_name2 file_name3
Jeśli masz wszystkie pliki, których możesz użyć
replace "old_string" "new_string" -- *
Jeśli masz listę plików z rozszerzeniem, możesz użyć
replace "old_string" "new_string" -- *.extension
replace "old_string" "new_string" -- *.txt
replace
narzędzie. Bez tych informacji odpowiedź jest niepełna.
„Możesz także użyć find i sed, ale uważam, że ta mała linia perla działa dobrze.
perl -pi -w -e 's/search/replace/g;' *.php
„(Fragment z http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.php )
Moje najlepsze wyniki pochodzą z używania perla i grepa (aby upewnić się, że plik ma wyrażenie wyszukiwania)
perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )
Biorąc pod uwagę, że chcesz wyszukać ciąg search
i zastąpić go replace
wieloma plikami, oto moja sprawdzona w bitwie formuła jednoliniowa :
grep -RiIl 'search' | xargs sed -i 's/search/replace/g'
Szybkie wyjaśnienie grep:
-R
- wyszukiwanie rekurencyjne-i
- bez rozróżniania wielkości liter-I
- pomiń pliki binarne (chcesz tekst, prawda?)-l
- wydrukuj prostą listę jako wynik. Potrzebny do innych poleceńDane wyjściowe grep są następnie przesyłane potokowo do sed (przez xargs), który jest używany do faktycznego zastąpienia tekstu. -i
Flaga zmieni plik bezpośrednio. Usuń go, aby uzyskać rodzaj trybu „pracy na sucho”.
Wymyśliłem własne rozwiązanie, zanim znalazłem to pytanie (i odpowiedzi). Szukałem różnych kombinacji „zamień”, „kilka” i „xml”, ponieważ taka była moja aplikacja, ale nie znalazłem tej konkretnej.
Mój problem: miałem wiosenne pliki XML z danymi dla przypadków testowych, zawierające złożone obiekty. Refaktor kodu źródłowego Java zmienił wiele klas i nie dotyczył plików danych XML. Aby zapisać dane przypadków testowych, musiałem zmienić wszystkie nazwy klas we wszystkich plikach xml, rozproszonych w kilku katalogach. Wszystko podczas zapisywania kopii zapasowych oryginalnych plików XML (choć nie było to konieczne, ponieważ kontrola wersji uratowałaby mnie tutaj).
Szukałem kombinacji find
+ sed
, ponieważ działało to dla mnie w innych sytuacjach, ale nie z kilkoma zastąpieniami na raz.
Potem znalazłem odpowiedź pytaj ubuntu i pomogło mi to zbudować linię poleceń:
find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
I działało idealnie (cóż, moja skrzynia miała sześć różnych zamienników). Pamiętaj jednak, że dotknie wszystkich plików * .xml w bieżącym katalogu. Z tego powodu, a jeśli jesteś odpowiedzialny za system kontroli wersji, możesz najpierw filtrować i przekazywać tylko sed
tym, którzy faktycznie mają ciągi, które chcesz; lubić:
find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;
findstr /spin /c:"quéquieresbuscar" *.xml
.
Naprawdę kiepski, ale nie mogłem zmusić żadnego z poleceń sed do działania bezpośrednio na OSX, więc zamiast tego zrobiłem to głupie:
:%s/foo/bar/g
:wn
^ - skopiuj te trzy linie do mojego schowka (tak, dołącz końcowy znak nowej linii), a następnie:
vi *
i przytrzymaj Command-V, aż powie, że nie ma już plików.
Głupi ... hacky ... skuteczny ...
Na MacBooku Pro użyłem następujących (inspirowanych https://stackoverflow.com/a/19457213/6169225 ):
sed -i '' -e 's/<STR_TO_REPLACE>/<REPLACEMENT_STR>/g' *
-i ''
zapewni, że nie będziesz wykonywać kopii zapasowych.
-e
dla nowoczesnego wyrażenia regularnego.
-e
po prostu mówi sed, że następny token jest poleceniem. W przypadku rozszerzonych wyrażeń regularnych użyj -E
zamiast tego.
Edytor strumienia modyfikuje wiele plików „w miejscu” po wywołaniu z -i
przełącznikiem, który przyjmuje plik kopii zapasowej kończący się jako argument. Więc
sed -i.bak 's/foo/bar/g' *
zastępuje foo
ze bar
wszystkich plików w tym folderze, ale nie zejść do podfolderów. Spowoduje to jednak wygenerowanie nowego .bak
pliku dla każdego pliku w katalogu. Aby zrobić to rekurencyjnie dla wszystkich plików w tym katalogu i wszystkich jego podkatalogach, potrzebujesz pomocnika, takiego jak find
, przechodzącego przez drzewo katalogów.
find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *
find
pozwala na dalsze ograniczenia plików, które należy modyfikować, podając dalsze argumenty, takie jak find ./ -name '*.php' -or -name '*.html' -print0
w razie potrzeby.
Uwaga: GNU sed
nie wymaga zakończenia pliku, sed -i 's/foo/bar/g' *
będzie również działać; FreeBSD sed
wymaga rozszerzenia, ale pozwala na spację między nimi, więc sed -i .bak s/foo/bar/g *
działa.
skrypt do komendy multiedit
multiedit [-n PATTERN] OLDSTRING NEWSTRING
Z odpowiedzi Kaspara zrobiłem skrypt bash, aby zaakceptować argumenty wiersza poleceń i opcjonalnie ograniczyć nazwy plików pasujące do wzorca. Zapisz w $ PATH i uczyń plik wykonywalny, a następnie po prostu użyj powyższej komendy.
Oto skrypt:
#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n
[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"
# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then # if -n option is given:
# replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
# replace OLDSTRING with NEWSTRING recursively in all files
find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi
Jeśli plik zawiera odwrotne ukośniki (zwykle ścieżki), możesz spróbować czegoś takiego:
sed -i -- 's,<path1>,<path2>,g' *
dawny:
sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)
Aby zachować mój osobisty angielski węzeł, napisałem skrypt narzędzia, który pomaga zastąpić wiele par starych / nowych ciągów dla wszystkich plików w katalogu rekurencyjnie.
Wiele par starych / nowych ciągów jest zarządzanych na mapie mieszającej.
Katalog można ustawić za pomocą wiersza polecenia lub zmiennej środowiskowej, mapa jest na stałe zakodowana w skrypcie, ale w razie potrzeby można zmodyfikować kod, aby ładował się z pliku.
Wymaga bash 4.2, ze względu na nową funkcję.
en_standardize.sh:
#! /bin/bash
# (need bash 4.2+,)
#
# Standardize phonetic symbol of English.
#
# format:
# en_standardize.sh [<dir>]
#
# params:
# * dir
# target dir, optional,
# if not specified then use environment variable "$node_dir_en",
# if both not provided, then will not execute,
# *
#
paramCount=$#
# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
echo -e "dir specified (in command):\n\t$1\n"
targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
targetDir=$node_dir_en
else # environable not set,
echo "dir not specified, won't execute"
exit
fi
# check whether dir exists,
if [ -d $targetDir ]; then
cd $targetDir
else
echo -e "invalid dir location:\n\t$targetDir\n"
exit
fi
# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")
# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'
# do replace,
for key in "${!itemMap[@]}"; do
grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done
echo -e "\nDone."
exit
Użycie polecenia ack byłoby znacznie szybsze:
ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'
Również jeśli w wyniku wyszukiwania masz białą spację. Musisz uciec.
Podaję przykład naprawy typowego błędu shebang w źródłach Pythona.
Możesz wypróbować podejście grep / sed. Oto taki, który działa z GNU sed i nie złamie repozytorium git:
$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}
Lub możesz użyć greptile :)
$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .
Właśnie przetestowałem pierwszy skrypt, a drugi również powinien działać. Uważaj na znaki ucieczki, myślę, że w większości przypadków powinno być łatwiej używać greptile. Oczywiście możesz robić wiele interesujących rzeczy za pomocą sed, a do tego może być lepiej opanować używanie go z xargs.
Znalazłem ten jeden z drugiego wpisu (nie pamiętam, który) i chociaż nie jest najbardziej elegancki, jest prosty i jako początkujący użytkownik Linuksa nie sprawił mi żadnych problemów
for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done
jeśli masz spacje lub inne znaki specjalne, użyj \
for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
dla ciągów w podkatalogach użyj **
for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done
Jest prostszy sposób, używając prostego pliku skryptu:
# sudo chmod +x /bin/replace_string_files_present_dir
otwórz plik w gedit lub edytorze do wyboru, używam gedit tutaj.
# sudo gedit /bin/replace_string_files_present_dir
Następnie w edytorze wklej następujące elementy do pliku
#!/bin/bash
replace "oldstring" "newstring" -- *
replace "oldstring1" "newstring2" -- *
#add as many lines of replace as there are your strings to be replaced for
#example here i have two sets of strings to replace which are oldstring and
#oldstring1 so I use two replace lines.
Zapisz plik, zamknij gedit , a następnie zamknij terminal lub po prostu zamknij go, a następnie uruchom, aby móc załadować nowy skrypt, który dodałeś.
Przejdź do katalogu, w którym masz wiele plików, które chcesz edytować. Następnie uruchomić:
#replace_string_files_present_dir
Naciśnij Enter, a to automatycznie zastąpi stary ciąg i stary ciąg 1 we wszystkich plikach, które je zawierają, poprawnym nowym ciągiem i newstring1 .
Pominie wszystkie katalogi i pliki, które nie zawierają starych ciągów.
Może to pomóc w wyeliminowaniu żmudnej pracy związanej z pisaniem w przypadku, gdy masz wiele katalogów z plikami, które wymagają zamiany ciągów. Wszystko, co musisz zrobić, to przejść do każdego z tych katalogów, a następnie uruchomić:
#replace_string_files_present_dir
Wszystko, co musisz zrobić, to upewnić się, że uwzględniłeś lub dodałeś wszystkie ciągi zastępujące, jak pokazałem powyżej:
replace "oldstring" "newstring" -- *
na końcu pliku / bin / replace_string_files_present_dir .
Aby dodać nowy ciąg zastępujący, wystarczy otworzyć skrypt, który utworzyliśmy, wpisując w terminalu:
sudo gedit /bin/replace_string_files_present_dir
Nie martw się o liczbę dodawanych ciągów zastępujących, nie będą one działać, jeśli nie zostanie znaleziony stary ciąg .
.gitlab-ci.yml
lub a travis.yml
). Każda kolejna zmiana polegająca na napisaniu skryptu w celu jego późniejszego wykonania jest anty-wzorcem, ponieważ wtedy będziesz musiał napisać skrypt do tworzenia skryptu (a ja przez większość czasu mam bałagan)
$ replace "From" "To" - `find / path / to / folder -type f`
(replace - narzędzie do zamiany napisów w systemie baz danych MySQL)