ZSH 함수의 문자열 길이는 항상 2를 반환합니다.

ZSH 함수의 문자열 길이는 항상 2를 반환합니다.

입력과 동일한 길이의 임의 문자열을 생성하여 고정 길이 텍스트 파일의 이메일 주소를 표시하려고 합니다. sed에서 문자열을 역참조로 전달하고 있습니다.

단순화하기 위해 이 스크립트(임시)는 다음과 같습니다.

#!/bin/zsh
IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing

#show me the input from the command line
echo $1 ${#1}

function randString() {
    # just echo for demonstration
    echo $1 ${#1}
    # this is the bit I really want:
    # cat /dev/urandom | LC_ALL=C tr -cd "[a-z]" | head -c${#1}
}

for line in $(cat $1); do
    echo $line |
        sed "s/\([a-zA-Z0-9]\{2,\}\)@\([a-zA-Z0-9]\{2,\}\)\.\([a-zA-Z0-9]\{2,\}\)/$(randString \\1)@$(randString \\2).$(randString \\3)/"

done

다음 데이터(temp.txt)를 사용하십시오.

me [email protected]
you [email protected]

다음과 같이 실행하세요:

./temp temp.txt

나에게 다음과 같은 출력을 제공합니다.

temp.txt 8
me myemail 2@someserver 2.com 2
you youremail 2@anotherserver 2.biz 2

문제는 ${#1}어떤 문자열을 입력해도 2가 반환된다는 것입니다. 길이 $1가 잘못된 올바른 문자열을 어떻게 반환할 수 있습니까 ${#1}? IFS파일 반복 설정으로 인해 기능이 종료됩니까?

참고: 저는 Mac을 사용하고 있으므로 GNU 확장이 없습니다.

답변1

일부 진단 비트는 무슨 일이 일어나고 있는지 보여줍니다.

이 줄을 함수 echo rand $1 ${#1} >&2에 추가하면 출력은 다음과 같습니다.randString

temp.txt 8
rand \1 2
rand \2 2
rand \3 2
me myemail 2@someserver 2.com 2
rand \1 2
rand \2 2
rand \3 2
you youremail 2@anotherserver 2.biz 2

stderr입력을 with 로 에코함으로써 역참조가 표시해야 하는 문자열 대신 인수 , 및 (길이 2)를 사용 하여 호출되고 있음 >&2을 알 수 있습니다 .randString\1\2\3

다음 테스트는 호출 앞에 sedwith를 추가하여 echo어떤 인수를 받는지 확인하는 것입니다. 출력:

sed s/\([a-zA-Z0-9]\{2,\}\)@\([a-zA-Z0-9]\{2,\}\)\.\([a-zA-Z0-9]\{2,\}\)/\1 2@\2 2.\3 2/

이런 식으로 sed문자열을 \1 2입력 문자열에 대한 역참조인 that과 같은 것으로 바꾸고 그 뒤에 공백과 숫자 2를 입력하라는 메시지가 표시됩니다. 입력 이메일 주소의 문자열은 함수 sed가 아닌 에서 옵니다.echo

이는 문자열이 인수로 전달되기 전에 $(...)문자열의 명령 대체(확장)가 처리되기 때문입니다 . 입력 문자열을 함수에 전달하려면 쉘 함수를 호출해야 합니다. 하지만 아마도 이렇게 할 방법은 없을 것입니다zshsedsed기본 버전sed.


추가하도록 편집됨: 기본적으로 이메일 주소 수정을 위한 빠른 스크립트 zsh:

#!/usr/bin/env zsh
setopt extendedglob

coproc cat /dev/urandom | LC_ALL=C tr -cd "[:lower:]" 

getRand (){
  print -r -- ${1//(#m)[[:alnum:]]/$(read -psk var;echo $var)}
}

while read line; do
  print -r -- ${line/(#m)[[:alnum:]](#c2,)@[[:alnum:]](#c2,).[[:alnum:]](#c2,)/$(getRand ${MATCH})}
done < ${1:?}

답변2

#!/bin/zsh
IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing

zsh -f이는 csh -f와일드카드를 비활성화하는 대신(sh/ksh 에뮬레이션 제외) set -o noglob또는 set +o glob(또는 setopt/ 를 사용한 변형 unsetopt) 읽기를 건너뛰는 것과 같습니다.

set -f인용되지 않은 확장에서 와일드카드를 수행하는 버그가 있는 기능을 해결하기 위해 다른 Bourne과 같은 쉘에서 이것을 사용할 수 있습니다 . 하지만 zsh globsubst에는 이 옵션이 기본적으로 비활성화되어 있으므로( sh/ ksh에뮬레이션 모드가 아닌 경우) 이러한 결함이 없습니다 .

#show me the input from the command line
echo $1 ${#1}

print -r -- $1 $#1또는 또는 또는 이어야 하며 , echo -E - $1 $#1s가 포함된 값이나 로 시작하는 일부 값에 대해서는 printf '%s\n' "$1 $#1"제대로 작동하지 않습니다 .$1\-

function randString() {

나는 randString() ...Bourne 구문이나 Korn 구문 function randString {중 하나를 선택 하지만 그 조합은 선택하지 않습니다(그러나 그것은 단지 취향의 문제입니다).

    # cat /dev/urandom | LC_ALL=C tr -cd "[a-z]" | head -c${#1}

개별 파일을 연결하는 데는 cat거의 의미가 없습니다 .

대부분의 tr구현 에서는 및 문자 tr -cd "[a-z]"도 생성됩니다 .[]

$ echo '[]123ab' | tr -cd '[a-z]'
[]ab
$ echo '[]123ab' | tr -cd a-z
ab
}

for line in $(cat $1); do

이는 쉘에서 텍스트가 처리되는 방식이 아닙니다. 바라보다쉘 루프를 사용하여 텍스트를 처리하는 것이 왜 나쁜 습관으로 간주됩니까?

    echo $line |
        sed "s/\([a-zA-Z0-9]\{2,\}\)@\([a-zA-Z0-9]\{2,\}\)\.\([a-zA-Z0-9]\{2,\}\)/$(randString \\1)@$(randString \\2).$(randString \\3)/"

거기에서 쉘은 $(...)그것을 호출하기 전에 확장을 수행 합니다 sed. 리터럴을 인수로 사용 하여 호출이 이루어 randString \\1지므로 결국 인수를 사용하여 호출됩니다.randString\1seds/\([a-zA-Z0-9]\{2,\}\)@\([a-zA-Z0-9]\{2,\}\)\.\([a-zA-Z0-9]\{2,\}\)/\12@\22.\32/

또한 무엇 [a-z]과 공동을 기록하십시오. 일치는 로케일에 따라 다릅니다.

여기서는 텍스트 처리 유틸리티(무작위 문자열을 생성할 수 있는 유틸리티)에 대한 호출을 실행해야 합니다. 그것은 다음과 같습니다:

#! /bin/sh -
exec perl -Tpe 's{\w{2,}@\w{2,}\.\w{2,}}{
  $& =~ s/\w/chr 96 + rand(26)/ger}ge' -- "$@"

여기에서는 as sh대신 사용하는 것으로 충분하며 텍스트 처리를 수행하고 임의 문자열을 생성하는 더 간단하고 효율적인 방법이 필요합니다.zshshperl

아니면 perl대신 스크립트를 작성하세요:

#! /usr/bin/perl --

while (<<>>) {
  s{\w{2,}@\w{2,}\.\w{2,}}{$& =~ s/\w/chr 96 + rand(26)/ger}ge;
  print;
}

여기서는 <<>>대신 사용되었습니다 <>. 해당 옵션은 이름이 지정된 파일이 아닌 프로세스의 출력과 같은 항목을 전달할 수 있는 루프를 -p의미합니다 .<>ls|lsls|꽤 위험하다. 이 옵션을 사용하면 -T보안 문제를 어느 정도 완화하고 해결할 수 있습니다.<<>>

내부적으로 비슷한 작업을 수행하는 zsh것이 가능 하지만 예쁘지는 않습니다.

#! /bin/zsh -
zmodload zsh/mathfunc
zmodload zsh/mapfile
set -o extendedglob

pattern='[[:alnum:]](#c2,)@[[:alnum:]](#c2,).[[:alnum:]](#c2,)'
for file do
  print -rn -- ${mapfile[$file]//(#m)$~pattern/${MATCH//(#m)[[:alnum:]]/${(L)$(( [##36] rand48() * 26 + 9))}}}
done

여기에 사용됨:

  • $mapfile[file]파일의 내용을 로드합니다.
  • zsh의 자체 glob 연산자는 일치를 위해 정규식을 대체합니다.
  • rand48()여기에서는 10에서 35 사이의 36진수로 난수를 생성하고 문자를 A에 출력 Z하고 매개변수 확장 플래그를 사용하여 소문자로 변환합니다 L.

관련 정보