Bash
從管道或命令行參數讀取文件名的 Bash 腳本?
我希望我的腳本讀取作為 glob 或從 STDIN 給出的一堆文件名(可能有空格)並用它們做一些事情。我已經能夠單獨閱讀任何一種方式,但不能將它們結合起來。
這從命令行讀取 glob:
for filename in "$@"; do process_file "$filename" done
這從 STDIN 讀取:
IFS=$'\n' read -d '' -r -a filenames for filename in "${filenames[@]}"; do process_file "$filename" done
我真正想要的是從其中一個讀取到一個數組中,這樣我就不必重複我的整個
for filename in...
循環兩次。抱歉,如果這很明顯,我是 BASH 的新手。編輯:我認為最好的辦法是從 args 中讀取它們,否則等待
STDIN
. 我該怎麼做?編輯:好的,問題不是我想的那樣。問題是 process_file 還要求使用者輸入。有沒有辦法從
STDIN
EOF讀取,儲存它,然後再次開始要求輸入?
新答案 2021-10-18
bash:從參數 和/或 標準輸入創建列表
考慮這個小腳本:
#!/bin/bash declare -a ARGS=("$@") while read -rt .02 arg;do ARGS+=($arg) done declare -p ARGS
你可以打電話給他們
argsAndInput.sh
樣品,然後…seq 1 10 | ./argsAndInput.sh foo bar baz declare -a ARGS=([0]="foo" [1]="bar" [2]="baz" [3]="1" [4]="2" [5]="3" [6]="4" [ 7]="5" [8]="6" [9]="7" [10]="8" [11]="9" [12]="10") seq 1 10 | ./argsAndInput.sh declare -a ARGS=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8" [8]="9" [9]="10") ./argsAndInput.sh foo bar baz declare -a ARGS=([0]="foo" [1]="bar" [2]="baz") ./argsAndInput.sh declare -a ARGS=()
根據您的需要,您可以減少超時 (
read -t .02
) 或使用:while read -t 0 && read -r arg;do ARGS+=($arg) done
如果您確定您的輸入將在您的 bash 執行之前準備好。
舊答案
您可以使用
xargs
將(即使有很多條目,比命令行緩衝區大得多)轉換為 arguments。STDIN
或者,也許是這樣的:
if [ $# -gt 0 ] ;then for filename in "$@" ; do process_file "$filename" done else IFS=$'\n' read -d '' -r -a filenames for filename in "${filenames[@]}"; do process_file "$filename" done fi
或兩者:
IFS=$'\n' read -d '' -r -a filenames for filename in "${filenames[@]}" "$@" ; do process_file "$filename" done fi
或通過添加這樣的選項,例如僅
-l
代表行參數,以確保不會從以下位置讀取(等待)STDIN
:case "$1" in -l ) shift for filename in "$@" ; do process_file "$filename" done ;; * ) for filename in "${filenames[@]}" "$@" ; do process_file "$filename" done ;; esac
(未經測試!)
當您未在命令行上指定任何文件名時,文本處理工具通常會從標準輸入中讀取輸入。您可以通過測試變數來檢查是否有命令行參數
$#
。假設process_file
如果您不向其傳遞參數,則從標準輸入讀取:if [ $# -eq 0 ]; then process_file else for x; do process_file "$x" done fi
或者,如果
process_file
需要參數,請嘗試將其傳遞/dev/stdin
給從標準輸入讀取:if [ $# -eq 0 ]; then set /dev/stdin; fi for x; do process_file "$x" done
當沒有命令行參數時,您要求從標準輸入中讀取文件名列表。當然,這是可能的,但我不建議這樣做,因為這很不尋常。您的使用者必須學習與大多數其他工具不同的約定。儘管如此,這是一種方法:
if [ $# -eq 0 ]; then set -f # turn off globbing IFS=' ' # set newlines to be the only field separator set -- $(cat) set +f; unset IFS fi for x; do process_file "$x" done