4

bashでこれをどのように達成しますか。これは私がインタビューで尋ねられた質問で、高級言語では答えを思いつくことができましたが、シェルでは思いつきませんでした。

私が理解しているように、実際の tail の実装はファイルの最後までシークしてから逆方向に読み取ります。

4

7 に答える 7

7

テール以外のすべてのコマンドが許可されている場合、気まぐれにならないのはなぜですか?

#!/bin/sh

[ -r "$1" ] && exec < "$1"

tac | head | tac
于 2013-09-03T19:56:41.293 に答える
4

wc -lファイル内の行数をカウントするために使用します。これから必要な行数を引き、1 を足して開始行番号を取得します。次に、これをsedまたはとともに使用しawkて、その行番号からファイルの印刷を開始します。

sed -n "$start,\$p"
于 2013-09-03T19:41:55.053 に答える
0

「純粋な」シェルで考えることができる唯一の方法は、while readファイル全体を行単位で配列変数に変換し、モジュロnでインデックス付けすることです。ここで、nは末尾の行の数 (デフォルトは 10) です。終了時に中断したところから循環バッファーを介してwhile read。これは効率的でも洗練されたものでもありませんが、機能し、ファイル全体をメモリに読み込むことを回避できます。例えば:

#!/bin/bash                                                                                 

incmod() {
    let i=$1+1
    n=$2

    if [ $i -ge $2 ]; then
        echo 0
    else
        echo $i
    fi
}

n=10
i=0
buffer=
while read line; do
    buffer[$i]=$line
    i=$(incmod $i $n)
done < $1

j=$i
echo ${buffer[$i]}
i=$(incmod $i $n)
while [ $i -ne $j ]; do
    echo ${buffer[$i]}
    i=$(incmod $i $n)
done
于 2013-09-03T19:48:00.983 に答える
0

実際に面接でこの質問をされた場合、私は次のように答えます。

bash私が持っているが、持っていない環境は何tailですか?おそらく、初期の起動スクリプトですか?busyboxシェル ユーティリティの完全な補完を使用できるようにそこに入ることができますか? あるいは、人生をずっと楽にするモジュールのほとんどがなくても、必要最小限の Perl インタープリターを詰め込むことができるかどうかを確認する必要があるかもしれません。dashよりもはるかに小さく、bashスクリプトでの使用に最適ですよね? それも役立つかもしれません。それができない場合は、静的にリンクされた C mini がどのくらいのスペースを必要とするかを確認するtail必要があります。必要なシェル スクリプトと同じ数のディスク ブロックに収まるはずです。

それがばかげた質問だとインタビュアーに納得させられない場合、私はbash拡張機能を使用することを信じていないことを観察します.最優先の懸念。一時的なものであっても移植できないものを避けることで、悪い習慣を身につけることはありません。実際のプログラミング言語で行うほうがよい場合でも、シェルで何かを行う誘惑に駆られることはありません。

問題は、真にポータブルなシェルでは、配列が利用できない場合があるということです。(POSIXシェル仕様に配列があるかどうかは実際にはわかりませんが、それらを持たないレガシーUnixシェルが確かに存在します。)したがって、シェル組み込みのみを使用してエミュレートする必要がありtail、どこでも動作する必要がある場合、これはあなたができる最善のことです、そしてそうです、あなたは間違った言語で書いているので、それは恐ろしいです:

#! /bin/sh

a=""
b=""
c=""
d=""
e=""
f=""

while read x; do
    a="$b"
    b="$c"
    c="$d"
    d="$e"
    e="$f"
    f="$x"
done

printf '%s\n' "$a"
printf '%s\n' "$b"
printf '%s\n' "$c"
printf '%s\n' "$d"
printf '%s\n' "$e"
printf '%s\n' "$f"

印刷する行数に一致するように変数の数を調整します。

戦場で傷ついたprintf人は、100% 利用できるわけではないことに気付くでしょう。残念ながら、あなたが持っているのが だけである場合echo、あなたは小川を上っています: のいくつかのバージョンはechoリテラル文字列 " -n" を出力できず、他のバージョンはリテラル文字列 " " を出力できません\n。特に、printf( POSIXにある)持っていない場合は、おそらくユーザー定義関数も持っていないためです。

(注:この回答のコードは、理論的根拠がなく、もともとユーザー「Nirk」によって投稿されましたが、一部のシェルには配列がないことに気付いていなかったと私が慈善的に想定する人々からの反対票の圧力の下で削除されました。)

于 2013-09-03T21:08:55.863 に答える
0

このスクリプトはどういうわけか模倣しtailます:

#!/bin/bash

shopt -s extglob

LENGTH=10

while [[ $# -gt 0 ]]; do
    case "$1" in
    --)
        FILES+=("${@:2}")
        break
        ;;
    -+([0-9]))
        LENGTH=${1#-}
        ;;
    -n)
        if [[ $2 != +([0-9]) ]]; then
            echo "Invalid argument to '-n': $1"
            exit 1
        fi
        LENGTH=$2
        shift
        ;;
    -*)
        echo "Unknown option: $1"
        exit 1
        ;;
    *)
        FILES+=("$1")
        ;;
    esac
    shift
done

PRINTHEADER=false

case "${#FILES[@]}" in
0)
    FILES=("/dev/stdin")
    ;;
1)
    ;;
*)
    PRINTHEADER=true
    ;;
esac

IFS=

for I in "${!FILES[@]}"; do
    F=${FILES[I]}

    if [[ $PRINTHEADER == true ]]; then
        [[ I -gt 0 ]] && echo
        echo "==> $F <=="
    fi

    if [[ LENGTH -gt 0 ]]; then
        LINES=()
        COUNT=0

        while read -r LINE; do
            LINES[COUNT++ % LENGTH]=$LINE
        done < "$F"

        for (( I = COUNT >= LENGTH ? LENGTH : COUNT; I; --I )); do
            echo "${LINES[--COUNT % LENGTH]}"
        done
    fi
done

実行例:

> bash script.sh -n 12 <(yes | sed 20q) <(yes | sed 5q)
==> /dev/fd/63 <==
y
y
y
y
y
y
y
y
y
y
y
y

==> /dev/fd/62 <==
y
y
y
y
y
> bash script.sh -4 <(yes | sed 200q)
y
y
y
y
于 2013-09-03T21:44:17.107 に答える