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を入力するように求められます。入力Eメールアドレスの文字列は関数で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と同じシェルでこれを使用できます。ただし、zshglobsubstにはこのオプションがデフォルトで無効になっているため(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

関連情報