단일 디렉터리의 경로를 사용하고 해당 디렉터리에 있는 모든 파일의 검색 문자열을 바꾸는 다음 스크립트를 만들었습니다. 외부 입력 파일에 나열된 여러 디렉터리의 문자열을 검색하고 바꿀 수 있도록 이 스크립트를 향상시키고 싶습니다.
외부 입력 파일 내용:
/var/start/system1/dir1
/var/start/system2/dir2
/var/start/system3/dir3
/var/start/system4/dir4
하나의 디렉토리가 있는 스크립트:
filepath="/var/start/system/dir1"
searchstring="test"
replacestring="test01"
i=0;
for file in $(grep -l -R $searchstring $filepath)
do
cp $file $file.bak
sed -e "s/$searchstring/$replacestring/ig" $file > tempfile.tmp
mv tempfile.tmp $file
let i++;
echo "Modified: " $file
done
답변1
sed -i
첫째, GNU sed
나 sed -i ''
FreeBSD(내부 교체) 와 함께 사용 하면 tmpfile 댄스를 피할 수 있습니다 .
grep -R
명령줄에서 여러 경로를 사용할 수 있으므로 경로에 공백이 포함되어 있지 않다고 확신하는 경우 $(grep -l -Re "$searchstring" "$filepath")
로 바꿀 수 있습니다 $(grep -l -R "$searchstring" $(cat path_list))
.
경로에 공백, 탭 또는 와일드카드 문자가 포함되어 있으면 이 작업은 실패하지만 원래 경로도 마찬가지입니다.
보다 강력한 접근 방식은 find
sed를 사용하고 이를 모든 파일에 적용하여 일치하지 않는 파일을 수정하지 않을 것이라고 믿습니다( bash
여기서 쉘을 가정).
# Read newline-separated path list into array from file 'path_list'
IFS=$'\n' read -d '' -r -a paths path_list
# Run sed on everything
find "${paths[@]}" \
-exec sed -i -r -e "s/$searchstring/$replacestring/ig" '{}' ';'
그러나 이는 수정 중인 파일에 대한 피드백을 제공하지 않습니다.
더 긴 버전은 피드백을 제공합니다.
# Read newline-separated path list into array from file 'path_list'
IFS=$'\n' read -d '' -r -a paths path_list
grep -l -R "$searchstring" "${paths[@]}" | while IFS= read -r file; do
sed -r -i -e "s/$searchstring/$replacestring/ig" "$file"
echo "Modified: $file"
done
답변2
GNU 도구 사용
< dir.list xargs -rd '\n' grep -rlZ -- "$searchstring" |
xargs -r0 sed -i -e "s/$searchstring/$replacestring/ig" --
(변수를 인용하는 것을 잊지 마세요. 인용되지 않은 변수는 분할+글로브 연산자입니다)
답변3
이것은 내가 생각할 수 있는 가장 이식성이 뛰어난 방법입니다.최대휴대용 /dev/fd/0
..dot
그것이 없으면 단일 파일을 사용할 수 있습니다. 어쨌든, 이는 제가 일전에 작성한 쉘 함수에 주로 의존합니다.
_sed_cesc_qt() {
sed -n ':n;\|^'"$1"'|!{H;$!{n;bn}};{$l;x;l}' |
sed -n '\|^'"$1"'|{:n;\|[$]$|!{
N;s|.\n||;bn};s|||
\|\([^\\]\)\\\([0-9]\)|{
s||\1\\0\2|g;}'"
s|'"'|&"&"&|g;'"s|.*|'&'|p}"
}
먼저 어떻게 작동하는지 보여주고, 그 다음에는 어떻게 하는지 설명하겠습니다. 따라서 테스트 저장소를 생성하겠습니다.
printf 'f=%d
echo "$f" >./"$f"
echo "$f" >./"$f\n$f"
echo "$f" >./"$f\n$f\n$f"
' $(seq 10) | . /dev/fd/0
그러면 각 파일에 포함된 숫자 1-10의 이름을 딴 여러 파일이 생성됩니다.
ls -qm
1, 1?1, 1?1?1, 10, 10?10, 10?10?10, 2, 2?2, 2?2?2, 3, 3?3, 3?3?3, 4, 4?4, 4?4?4, 5, 5?5, 5?5?5, 6, 6?6, 6?6?6, 7,
7?7, 7?7?7, 8, 8?8, 8?8?8, 9, 9?9, 9?9?9
이것은 내 테스트 디렉터리에 있는 쉼표로 구분된 파일 목록이며, 각 파일은 ?
개행을 나타냅니다.
cat ./1*
1
1
1
10
10
10
각 파일에는 하나의 숫자만 포함됩니다.
이제 다음을 교체하겠습니다 grep
.
find ././ \! -type d -exec \
grep -l '[02468]$' \{\} + |
_sed_cesc_qt '\./\./' |
sed 's|.|\\&|g' |
xargs printf 'f=%b
sed "/[02468]\\$/s//CHANGED/" <<-SED >"$f"
$(cat <"$f")
SED\n' |
. /dev/fd/0
이제 내가...
cat ./1*
1
1
1
1CHANGED
1CHANGED
1CHANGED
모든 [2468]
파일은 유사합니다 CHANGED
. 또한 재귀적으로 작동합니다. 좋아요, 이제 방법을 설명하겠습니다.
먼저 이 기능은 다음과 같습니다.
:n
확장 태그로 시작\|
주소|
매개변수$1
- 토큰- 현재 행이
!
일치하지 않는 경우{
H
이전 버퍼 에 추가!
현재 줄이 마지막$
줄 이 아닌 경우{
- 확장된 행으로
n
현재 행 덮어쓰기 b
랜치 백:n
확장 태그}}
- 그렇지 않고 현재 줄이
$
마지막 줄 이라면l
패턴 공간을 살펴보세요. - 그렇지 않으면 e는
x
홀드 및 패턴 버퍼의 내용을 변경하고... l
패턴 공간을 명확하게 살펴보세요
이것이 첫 번째 sed
진술입니다. 이것이 바로 핵심입니다. 우리는 p
패턴 공간을 전혀 인쇄 하지 않고 단지 l
보기만 합니다. l
POSIX가 함수를 정의하는 방법은 다음과 같습니다 .
[2주소]
l
(이 편지는 ell입니다.)시각적으로 명확한 형식으로 패턴 공간을 표준 출력에 기록합니다. IEEE Std 1003.1-2001 기본 정의 볼륨 표 5-1, 이스케이프 시퀀스 및 관련 작업에 나열된 문자는( '\\', '\a', '\b', '\f', '\r', '\t', '\v' )
해당 이스케이프 시퀀스로 작성되어야 하며 이 표의 내용은'\n'
적용되지 않습니다. 이 표에 없는 인쇄할 수 없는 문자는세 자리\
문자에 있는 각 바이트의 8진수(앞에 백슬래시가 옴)입니다(가장 중요한 바이트부터). 긴 줄은 접어야 하며, 접는 지점은\
백슬래시 뒤에 슬래시 를 써서 표시됩니다\n
. 접는 길이는 지정되지 않지만 출력 장치에 맞아야 합니다. 각 줄의 끝은 으로 표시되어야 합니다'$'
.
그렇다면 다음과 같이 하세요.
printf '\e%s10\n10\n10' '\' | sed -n 'N;N;l'
나는 얻다:
\033\\10\n10\n10$
그것은 거의 완벽한 탈출이었다 printf
. 단지 8진수에 0을 추가하고 후행을 제거하므로 $
다음 sed
명령문에서는 이를 지웁니다.
동일한 세부 사항을 다루지는 않겠지만 기본적으로 다음 sed
설명은 다음과 같습니다.
- 행이
$1
태그로 시작하는 경우... N
현재 줄이 끝날 때까지 추가 줄을 끌어옵니다.$
\
위 작업을 수행해야 하는 경우 후행 백슬래시와 줄\n
바꿈 문자가 제거됩니다 .- 그런 다음 후행을 제거합니다.
$
\
백슬래시 뒤에 숫자가 오고 다른 백슬래시가 앞에 오지 않는 것을 찾아\
0을 삽입하세요.- 작은따옴표를 검색
'
하고 큰따옴표를 사용하세요. '
마지막으로 전체 문자열을 작은따옴표로 묶습니다.
이제 이 작업을 수행하면 다음과 같습니다.
printf %s\\n ././1* |
_sed_cesc_qt '\./\./'
나는 얻다:
'././1'
'././1\n1'
'././1\n1\n1'
'././10'
'././10\n10'
'././10\n10\n10'
나머지는 쉽습니다. 이는 문자열이 구문 분석된다는 사실에 따라 다르지만 출력 ././
에서는 각 경로 이름의 시작 부분에만 표시되므로 표시자가 됩니다.find/grep
$1
정규 표현식이 포함된 파일의 파일 이름을 출력하도록 -exec grep
시작 find
하고 지정했습니다 .-l
함수를 호출하고 출력을 얻습니다.
그런 다음 \
백슬래시로 출력의 모든 문자를 이스케이프 처리합니다 xargs
.
파일 printf
에 스크립트를 작성합니다 . 소스 를 로 지정 합니다. 변수를 현재 매개변수(내 경로 이름)로 정의하고 해당 매개변수를 에 제공되는 heredocument 에 전달 하고 소스 파일에 다시 작성합니다.|pipe
.dot
/dev/fd/0
f
cat
$f
<<
sed
sed
여기에는 임시 파일이 포함될 수 있으며 쉘에 따라 다릅니다. 각 여기 문서에 대한 임시 파일을 bash
작성 zsh
하지만 정리도 수행합니다. dash
반면 이곳의 문서만 익명으로 작성됩니다 |pipe
.
그러나 파일을 쓰기 전에 파일을 완전히 읽어야 하는 것이 중요합니다. 이것이 문서 및 명령 대체가 작동하는 방식입니다.