スクリプト/コマンドラインでアルファベット順などに基づいて現在のディレクトリからファイルのサブ範囲を選択する方法は?

スクリプト/コマンドラインでアルファベット順などに基づいて現在のディレクトリからファイルのサブ範囲を選択する方法は?

ファイルマネージャでは、通常、ファイルを選択し、他のShiftファイルを長く選択できます。その間のすべてのファイルが選択されます。

私はしたいbash/zshこれと同等である。

例:2つのファイル名を指定したいと思います。各ファイル名は中央にあります(アルファベット順 -ls出力方式)。

そして他のワイルドカードオプションを知っていますが、名前が? {} *非常に混乱しているファイルに使用できることを願っています。

たとえば、与えられたファイル

$ ls
aoeitoae.txt
oaeistn.txt
oaie.txt
paeoai.txt
sotaoe.txt

次のコマンドを実行して次のようにしますrm aoeitoae.txt-oaie.txt

$ ls
paeoai.txt
sotaoe.txt

この目標をどのように達成できますか?

答え1

この答えは主にzshについてです。ほとんどの操作はbashでは簡単にはできません。

多くの一般的な状況で使用できます。ワイルドカード。特に:

  • すべてが付属固定プレフィックス:foo-*
  • すべてが付属固定プレフィックスの後に範囲内の他の文字が続きます。:(、、、をfoo[b-m]*含むが、または除く)foobarfoodfoomz1fooarfoon
  • 特定の範囲内の数:(IMG-<7-42>.*含むIMG-8.PNGIMG-0042.JPG除くIMG-77.JPG

そしてグローバル予選、識別する簡単な方法があります。範囲ですが、数が必要です:ファイルが何であれ、リストされてfoo*([1,3])いる最初の3つのファイルと一致しますfoo*(3つ未満の場合はすべてのファイルと一致します)。foo*(om[1,3])これは、で始まる名前の一致などのソートが完了した後に発生しますfoo

zshに数字を調べるように依頼できます。 2つのステップで行います。まず、すべての一致を配列に配置し、次を使用します。下付き文字記号 i(ワイルドカードの一致を防ぐI場合e):要素間の配列部分(含まれて$a[$a[(i)foo],$a[(I)bar]]いる)または存在しない場合は空です。$afoobarfoobar

a=(*.txt(oL))
# List the files that appear between small.txt and large.txt in a listing by size.
# Note that files that have the same size as one of the bounds may or may not be included.
echo $a[$a[(I)small.txt],$a[(I)large.txt]]

だからこれは実装する関数です。質問の要件を完全に満たしています。(正確な構文を除いてこれを行うことはできません):

# Usage: select_range FROM TO WILDCARD_PATTERN
# Sets the array $s to the files matching PATTERN from FROM to TO inclusive.
function select_range {
  if (($# < 2)); then
    echo >&2 "select_range: missing range arguments"
    return 120
  fi
  local from=$1 to=$2
  shift 2
  from=$@[(ie)$from]
  if ((from == 0)); then
    echo >&2 "select_range: not matched: $from"
  fi
  to=$@[(Ie)$to]
  if ((to == 0)); then
    echo >&2 "select_range: not matched: $from"
  fi
  s=($@[$from,$to])
}

使用法:select_range aoeitoae.txt oaie.txt * && rm $s

globe修飾子を使用すると、任意のコードを書いて結果をフィルタリングできますが、少し扱いに​​くくなり始めました。複雑なケースでは、引用は難しいかもしれません。簡略化のために'区切り記号(バックスラッシュとして引用する必要があります)として使用し、フィルタコードを一重引用符で囲んでパターンを次に示しますfoo-*(e\''code goes here'\')。 (引用が複雑すぎる場合は、関数を作成して修飾子を使用してください+。)フィルタリングaoeitoae.txt前とフィルタリング前のファイルoaie.txt アルファベット順*(e\''! [[ $REPLY < aoeitoae.txt || $REPLY > oaie.txt ]]'\')

フィルタで実行された比較は、必ずしもワイルドカード拡張と同じ順序を使用するわけではありません。たとえば、thanks修飾子はfoo-*(n)前にリストされていますが、文字列比較ではfoo-9foo-10n[[ foo-9 > foo-10 ]]ホームフレーズ整数部分文字列の数値比較に似た>演算子です。一つ作りたいなら文字列と数値でソートされた整数部分の比較、あなたはそれを使用することができますn パラメータ拡張フラグ配列を並べ替え、中間に一致する名前が保持されていることを確認します。 、、、、...*(ne\''a=(b11r $REPLY f10o); [[ $a[2] == "${${(@n)a}[2]}" ]]'\'))は含まれていますが、、、...は含まれていません。b101rb11sd1f02ob9rf011

日付別にファイルを一致させる場合は、条件を使用できます(ファイルは独自のファイル-ntよりも最新ではありません)。*(ome\''! [[ $REPLY -ot from || $REPLY -nt to ]]'\')間に修正されたファイルの修正時間fromとの修正時間to(含む)です。

答え2

Bash機能を使う:

sfiles ()
(
    # run this in a subshell, so we don't have to care if nullglob/dotglob were enabled or not
    [ $# -eq 0 ] && exit
    
    local nullsep=0
    if [ "$1" = "-0" ]; then
        nullsep=1; shift
    fi
    local first=$1
    shift $(($# -1))
    local last=$1
    local files=( )

    shopt -s nullglob dotglob
    for i in *; do
        # first argument found or array not empty?
        if [ "$i" = "$first" ] || [ "${#files[@]}" -ne 0 ]; then
            files+=( "$i" )
        fi
        # last argument found? break loop
        [ "$i" = "$last" ] && break
    done

    if [ "${#files[@]}" -gt 0 ]; then
        [ "$nullsep" -eq 1 ] && 
            printf '%s\0' "${files[@]}" ||
            printf '%s\n' "${files[@]@Q}"
    fi
)

最初の引数と最後の引数(含む)の間のすべてのファイルを出力します。

例:

$ ls -A
 btjhyyxrlv.txt    otewagahzp.txt       .xxx
 crlcsbzizl.txt    ssffszhdmp.txt      'zdjtgahx q.txt'
 hgiagchkgt.txt   'tt'$'\t''aa.txt'    'zmwik zhur.txt'
 jusupbivit.txt    umikyfucgu.txt      'z otmleqlq.txt'
' kcyigyurc.txt'  ' upvpntdfv.txt'      .zzz
 kfthnpgrxm.txt   'uu'$'\t\t''aa.txt'
 lgzsmquxwj.txt    wlwexgzohs.txt
$ sfiles c* k*
'crlcsbzizl.txt'
'hgiagchkgt.txt'
'jusupbivit.txt'
' kcyigyurc.txt'
'kfthnpgrxm.txt'
$ sfiles .xxx .zzz
'.xxx'
'zdjtgahx q.txt'
'zmwik zhur.txt'
'z otmleqlq.txt'
'.zzz'
$ LC_ALL=C sfiles .xxx .zzz
'.xxx'
'.zzz'

無効な順序です。何も返しません。

$ sfiles .zzz .xxx

選択したファイルを削除するには、次のコマンドを使用しますxargs

$ sfiles .xxx .zzz | xargs rm

タブまたは改行を含むファイル名の場合は、-0bash引用符なしでnullで区切られた出力の最初の引数としてオプションを追加します。

$ sfiles -0 tt* uu* | xargs -0 ls
'tt'$'\t''aa.txt'  ' upvpntdfv.txt'
 umikyfucgu.txt    'uu'$'\t\t''aa.txt'

答え3

これを行うには、独自のシェル関数を作成できます。すべてのファイルのリストを取得し、最初のファイル名の前のすべてのファイルと2番目のファイル名の後のすべてのファイルを削除します。おおまかに言えば、zshはこれがbashでもっと迷惑になると思うからです。

#!/usr/bin/zsh
filerange() {
  # variable "reply" is an array
  typeset -ag reply
  reply=("$1")
  # don't get upset if nothing matches
  setopt nullglob

  # get a complete list of candidate files
  #allfiles=*(^/^on) 
  #         ^--------- *   glob
  #          ^----^--- (…) glob modifiers within parentheses
  #           ^------- ^   invert match
  #            ^------ /   match directories (inverted: match all *but* dirs)
  #             ^----- ^   invert sort order (uninvert the above inversion)
  #              ^---- o   order by:
  #               ^--- n    character values of name
  for fname in *(on^/); do
    [[ ( "$2" > "${fname}" && "${fname}" > "$1" ) ]] \
    && reply+=("${fname}")
  done
  reply+=("$2")
}

$replyこれは、たとえば、使用できる素敵で「安全な」配列を提供しますfor

# prepare a test dir
mkdir /tmp/testdir
cd /tmp/testdir
sudo mknod blockdev b 1 1
sudo mknod chardev c 1 1
mkdir dir
mkfifo fifo
touch file
touch "file with spaces"
touch "file with\nnewlines"
touch "last file"
touch "ok one more file"
ln -s blockdev symlink

# This will look funny, because there's a file name with a new line in there
echo *

# Let's try this:
filerange chardev "last file"
# We're expecting to get all files from (incl) chardev to (incl) last file,
# but not `dir`, as that's a directory
for f in ${reply}; do;
  echo "entry: ${f}"
done

parallelもちろん、これらのファイル(forやなど)を印刷したい場合は、Freddyのファイルとしてオプションを使用する関数を作成してくださいxargs-0良い答えイラストは抜群の選択です!この関数はとても簡単です。 'ingループから呼び出すことができ、fromをfilerange処理します。$replyprintffor

答え4

これは完全な答えではありませんが、awk使用できる優れた範囲演算子があることに言及したかったです。

$ ls | awk '/^aoeitoae\.txt$/,/^oaie\.txt$/ { print }'
aoeitoae.txt
oaeistn.txt
oaie.txt

このawkコードは、最初の正規表現に一致する最初の入力行から始まり、2番目の正規表現に一致する最初の入力行で終わる入力行を印刷します。

マッチングは正規表現を使用し、この例では引用されていないため、ファイル名にスペースやその他の奇妙な文字が含まれているため、予期しない結果が生じる可能性があります。

そしておそらく次のテキストベースのファイルマネージャを試してみたいです。真夜中司令官

関連情報