awk内にファイルが存在するかどうかをどのように確認しますか?[-d 'ファイル名']失敗


10

存在しないホームディレクトリセットを持つユーザーのリストを生成しようとしています。これはawkで実行できるはずですが、構文に問題があります。

]で「無効な構文」と何度も言われます。何が悪いのですか?

awk -F: '{ if(![ -d "$6"]){ print $1 " " $3 " " $7}}' /etc/passwd

私がおそらく最終的に使用するコードは次のとおりです。

awk -F: '{if(system( "[ -d " $6 " ]") == 1 && $7 != "/sbin/nologin" ) {print "The directory " $6 " does not exist for user " $1 }}' /etc/passwd

そして、私はここに関連する質問があります

回答:


14

あなたは使うことができます

system(command) 
    オペレーティングシステムコマンドcommandを実行し、次に
    awkプログラムに戻ります。コマンドの終了ステータスを返します。 

例えば:

awk -F: '{if(system("[ ! -d " $6 " ]") == 0) {print $1 " " $3 " " $7}}' /etc/passwd

何らかの理由でこれを機能させることができなかったため、これを行う必要がありました。awk '{if(system( "stat" $ 0 "> / dev / null 2> / dev / null")== 1)print $ 0 } '
AlexanderKjäll17年

1
@AlexanderKjäll-これはディレクトリの存在をテストします- 通常のファイルが存在するかどうかを確認しようとしている場合、上記のコードが失敗することは明らかです...
don_crissti

3
これは非常に危険です。 入力によっては、任意のコマンドが実行される場合があります。たとえば、次の実行/bin/lsecho ';ls;' | awk '{ print system("[ ! -d " $1 " ]") }'
ティノ

6

それはシェルのことだと[ -d ]は思いませんawk。代わりに私はこのようにします:

awk -F: '{ print $1,$3,$7}' /etc/passwd | 
    while read name uid shell; do 
        [ -d "/home/$name" ] || echo "$name $uid $shell"; 
    done

もちろん、@ Janisが正しく指摘しているように、シェルですべてを行うことができます。

while IFS=: read  name x uid x x x shell rest; do
     [ -d "/home/$name" ] || echo "$name $uid $shell" 
done < /etc/passwd

わかりました、私は実際に$ 6をdirとして渡し、-d $ dirを実行したいと思いますが、これは非常に役立ちます。何も見つからない場合に「結果なし」をエコーする方法を見つけなければなりません。ありがとうございました!
Doug

2
awk実際には必要ないことに注意してください。とにかくシェルでループするので、あなたも行うことができますwhile IFS=: read -r name x uid x x x shell rest ; do ... ; done </etc/passwd
Janis、

@Dougただやるawk -F: '{print $6}' /etc/passwd | while read dir; do [ -d "$dir" ] || echo "no results for $dir"; done
terdon

@Janis確かに、良い点、回答が編集されました。
terdon

@terdonが指摘しているように、[ -d "$6"]実際にはawkではなくbash構文です。[(bashの/ Linuxののより良いweirdnessesのいずれかで)通常の構文のように見えるが、それは実際の同義語だtest実行可能プログラム(または多分ビルトインそれのバージョンのbash、そして、ただ奇妙であるためには、bashはマッチングが必要です]そのdoesnのを全体が本当に構文でありプログラムではないと誤解させる以外に、私が知っていることは何もしないでください)。いずれにせよ、これはawkが知っていることではありません。そのため、system()理解されているbashのコンテキストで関数を参照してアクセスするための関数が必要です
Joe

6

getlineを使用できます。

awk 'BEGIN {print getline < "file" < 0 ? "not exists" : "exists"}'

1
これはで安全ですがawk、残念ながらディレクトリでは機能しません。
Tino

5

実際に使用している場合gawk(ただし、、nawkまたはを使用mawkしている場合がありますが、この場合は適用されません)、v4.0以降で使用可能なロード可能な拡張機能の 1つを使用してネイティブに実行できます。私は使用しています(v4.0は拡張機能をロードするための構文にバリエーションがありました)。gawk-4.1.x

filefuncs拡張機能をロードすると、(特に)stat()関数が追加されます。

@load "filefuncs"
BEGIN {FS=":"}
(NF==7) {
   printf("user: %s %i %i\n",$1,$3,$4)
   rc=stat($6,fstat)
   err=ERRNO  # ERRNO is a string, not an int!
   if (rc<0) { 
       printf(" error: %s rc=%i %s\n",$6,rc,err)
   } else {
      if (fstat["type"]!="directory") 
        printf("  ENOTDIR: %s %s\n",$6,fstat["type"])
      if (fstat["uid"]!=$3) 
        printf("  uid mismatch: %s %i!=%i\n",$6,fstat["uid"],$3)
      if (fstat["gid"]!=$4) 
        printf("  gid mismatch: %s %i!=%i\n",$6,fstat["gid"],$4)
   }
}

filefuncs(3am)この拡張機能の詳細については、manページを参照してください。

次のようなもので実行します:

gawk -f testhome.awk <(getent passwd)    # bash/zsh and glibc
gawk -f testhome.awk /etc/passwd

gawkバイナリが次の拡張機能をサポートしていることを確認できます。

BEGIN { 
  if (!("api_major" in PROCINFO)) 
    printf("No extension API.\n")
  else
    printf("Extension API v%s.%s.\n",PROCINFO["api_major"],PROCINFO["api_minor"])
}

余談:ファイルgawkを読み取るための小さなライブラリ関数も付属しておりpasswd、次のように呼び出すことができます。

gawk -i passwd.awk -- 'BEGIN { while(uu=getpwent()) {print uu;} endpwent(); }'

getentnsswitchをサポートしているので、Linux / glibcシステムで使用することを好みます。


1
面白い。私はgawkv4がこれを提供していることを知りませんでした。ありがとう!
Tino、

3

それはほとんどおかしいです...

perl -F: -ane 'if(!-d $F[5]){ print "$F[0] $F[2] $F[6]" }' /etc/passwd

0

ここに解決策があります

  • 用途gawk/bin/sh
  • いくつかの外部コマンドでディレクトリチェックを行います
  • しかし、それは安全で安全な方法です

コード:

gawk -F: '{
    cmd="IFS=\"\" read -r dir; [ -d \"$dir\" ]; echo $?";
    print $6 |& cmd;
    cmd |& getline x;
    close(cmd);
    if (x) print x " " $1 " " $3 " " $7
}' /etc/passwd

説明:

  • IFS="" read -r dir; [ -d "$dir" ]; echo $?stdinからパスを読み取り、0それがディレクトリであるかどうかを出力するシェルコードです。1
  • print $6 |& cmdファイル名をコマンドにパイプします。|&GNU-awkの拡張機能です。
  • cmd |& getline x コマンドの出力をGNU-awkに読み込みます
  • close(cmd) コマンドを終了するので、次の行でもう一度実行できます
  • if (x)実行printする場合にのみxではありません0(ディレクトリが存在しないので)

ただし、この方法は非常に遅く、扱いにくいので、お勧めしません。しかし、このレシピは安全であり、不規則な入力は害を及ぼしません。(/etc/passwd有害なデータが含まれている可能性は低いですが、おそらく誰かが信頼できないソースからのデータでそれを使用したいと考えています)。

を使用できない場合gawk、これは残念です。その場合はありません|&。通常awkは、次の3つのいずれかを実行できます。

  • print "data" | cmd; close(cmd):データをコマンドにパイプする
  • getline data < cmd; close(cmd):コマンドからデータを読み取る
  • ret = system(cmd):コマンドの戻りコードを取得します

「ノーマル」はawk単純にデータをスクリプトにパイプして同時にそれから何かを取得することはできません(少なくとも私はその方法を見つけられませんでした)ため、さらに不格好な中間ファイル(tempfile)が必要です。

興味深い観察は、そのような簡単なタスクはシェルだけでも実行できるということです:

dump_problems()
{
have=0;
while IFS=: read -r user pw id grp gcos dir shl;
do
  [ -d "$dir" ] && continue;
  echo "$user $id $shl";
  have=1;
done </etc/passwd;
return $have;
}

dump_problems && echo ALL OK >&2 || echo "Problems were found" >&2

必要はありません。bash通常の各Bourneシェルは上記のコードを実行できます。

上記のシェルコードは実際に必要なものよりも少し複雑ですが、これは実際にシェルコードを操作する方法の指針となることに注意してください(シェルがどのように機能するかについて十分に経験していない人にとって)。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.