구분된 텍스트 데이터 가 있고 "|"
열 값을 변환하고 싶습니다.
$ cat infile
Mark|father
Jason|SOn
Jose|son
Steffy|daugHter
(아버지 | 아들 | 딸)의 사례를 무감각하게 검색하여 아버지가 아버지인 경우, 아들이 아들인 경우, 딸이 딸인 경우를 바꾸고 싶습니다.
따라서 출력 파일은 다음과 같아야 합니다.
$ cat outfile
Mark Father
Jason Son
Jose Son
Steffy Daughter
IGNORECASE와 sub 또는 gsub의 다양한 조합을 시도하고 있지만 모든 항목이 infile로 인쇄됩니다.
답변1
이것은 시도된 답변입니다질문의 원래 버전. 그 이후로 요구 사항이 변경되었습니다.
sed
GNU 구현의 장점 중 하나 는 다음과 같습니다 .
$ sed -E 's/(^|\s)(son|daughter|father)(\s|$)/\1\L\u\2\3/i' < file
Mark Father
Jason Son
Jose Son
Steffy Daughter
정규식은 이 3개 단어 중 하나와 일치하지만 그 단어 앞에 공백이 아닌 문자가 없는 경우에만 일치합니다.
\L
전체 단어를 소문자로 변환하고 \u
첫 번째 문자만 대문자로 변환합니다(이것은 ex
70년대 의 것이지만 vi
불행하게도 par까지가 아닙니다 sed
).
perl -pe
대신 동일한 것을 사용할 수 있습니다 ( GNU보다 sed -E
더 많은 시스템에 잠재적으로 이식성이 더 높음 ). 하지만 다음과 같이 단순화할 수 있습니다.perl
sed
perl
perl -pe 's/(?<!\S)(son|daughter|father)(?!\S)/\L\u$&/i'
즉, 이러한 문자열이 공백으로 구분된 긴 단어(예: Jason
입력에서)의 일부가 아닌지 확인하려면 음수 탐색 연산자를 사용하십시오. sed 의 \b
in perl
및 word 경계 연산자 도 참조하세요. 그러나 이는 문자를 구성하는 단어가 아니기 때문에 손자를 손자로 바꾸는 것과 비슷합니다.\<
\>
(?!\w)
-
각 줄은 최대 한 번만 바꿀 수 있습니다. 모든 항목을 바꾸려면 g
위의 플래그에 플래그를 추가 할 수 있습니다 perl
. 첫 번째 일치 항목이 다음으로 대체되고 검색 이 계속되어 이전 일치 항목이 발견되지 않기 때문에 하나 에 추가하면 sed
일부가 손실될 수 있습니다 . 이 문제는 사전에 모든 공백 문자를 두 배로 늘리고 나중에 복원하여 해결할 수 있습니다.Mark son SON sOn
" son "
" Son "
sed
"SON sOn"
\s
SON
sed -E 's/\s/&&/g
s/(^|\s)(son|daughter|father)(\s|$)/\1\L\u\2\3/ig
s/(\s)\1/\1/g'
하지만 이것은 조금 너무 복잡해지기 시작했습니다.
답변2
효율성과 견고성을 위해 정규식 비교 및 *sub() 대신 해시 조회를 사용합니다(정규식 메타 문자나 역참조가 포함된 문자열 또는 다른 문자열 하위 문자열을 사용하기로 결정한 경우).
$ cat tst.awk
BEGIN {
FS = "|"
split("Father|Son|Daughter",tmp)
for (i in tmp) {
map[tolower(tmp[i])] = tmp[i]
}
}
{ lc = tolower($2) }
lc in map {
$2 = map[lc]
}
{ print }
$ awk -f tst.awk file
Mark Father
Jason Son
Jose Son
Steffy Daughter
답변3
(모든 awk 구현에 적용되는) 한 가지 접근 방식은 두 번째 열을 소문자로 만들고 첫 번째 문자만 대문자로 만든 다음 일치하는지 확인한 다음 두 번째 열의 값을 저장된 변환된 내용으로 업데이트하는 것입니다.tmp.
$ awk -F'|' '{ tmp=toupper(substr($2,1,1)) tolower(substr($2,2)) }
tmp ~ /^(Father|Son|Daughter)$/ { $2=tmp }1' infile
Mark Father
Jason Son
Jose Son
Steffy Daughter
(GNU awk 특정) 을 사용할 때 IGNORECASE
이는 교체할 때가 아니라 수행하려는 모든 일치 처리(문자열/정규식)에만 적용됩니다.
답변4
Raku(이전 Perl_6) 사용
raku -pe 's:i:g/ «father» | «daughter» | «son» /{$/.tclc}/;'
또는
raku -pe 's:i:g/ «father» | «daughter» | «son» /{$/.wordcase}/;'
정규식 부사는 :ignorecase
Raku(약어)에서 대소문자를 구분하지 않는 일치를 수행합니다. :i
왼쪽 및 오른쪽 단어 경계는 전체 단어만 일치하도록 보장합니다(즉, 유사한 출력이 발생할 수 있는 가짜 일치가 없음 «
). 왼쪽 단어 경계에는 대체를 사용하고 오른쪽 단어 경계에는 대체를 사용할 수 있습니다.»
JaSon
<<
«
>>
»
대소문자를 변경하기 위해 Raku에는 wordcase
단어를 가져와 첫 글자를 대문자로 바꾸고 첫 글자가 아닌 모든 글자를 소문자로 변환하는 멋진 루틴이 있습니다. [Raku 기능 tclc
(문자 그대로 "titlecase-lowercase")은 기본적으로 동일한 작업을 수행하지만 옵션이 더 적습니다.]
입력 예:
Mark|father
Jason|SOn
Jose|son
Steffy|daugHter
Agnes|moTHer
예제 출력:
Mark|Father
Jason|Son
Jose|Son
Steffy|Daughter
Agnes|moTHer
예를 들어 OP가 구분 기호로 분할하려는 경우 |
다음 Raku 한 줄 문자를 호출하면 됩니다.앞으로또는뒤쪽에위의 코드:
raku -ne '.split("|").put;'
예제 출력:
Mark Father
Jason Son
Jose Son
Steffy Daughter
Agnes moTHer
부록:
@Stéphane Chazelas는 위의 코드(예를 들어)에서 하이픈으로 연결된 단어가 내부 대문자(예를 들어 god-son
to god-Son
)를 갖게 된다는 점을 주석에서 지적했습니다. 아래 코드는 이 문제를 피하기 위해 세 가지 리터럴 일치를 사용합니다.
raku -ne '.wordcase(:where({ $_.fc eq "father" | "daughter" | "son"})).put;'
또는
raku -pe '.=wordcase(:where({ $_.fc eq "father" | "daughter" | "son"}));'