我想縮短我的一個腳本,我不知道該怎麼做。有一段這樣的程式碼:
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
當然這是簡短的版本:) 現在我希望能夠縮短它,而不是到處寫 $COMMAND,所以我想要這樣的東西:
$COMMAND <the case statement return value>
但我不想使用變數來儲存案例的結果。
謝謝:)希望你明白我想要什麼:D
編輯 1:可以建立一個函數並將 find 參數作為 Serg 指出的 $1 傳遞。現在,如果我想在沒有函數的情況下做到這一點,我確信有一種方法:D 不像 Serg 沒有解決它,我只是好奇:D
答案1
有幾種可能的方法可以簡化程式碼。以下是按代碼量排序的解決方案:
printf
和參數替換(無錯誤檢查)xargs
和參數替換(無錯誤檢查)find
僅參數替換(無錯誤檢查)- 失敗案例結構(解決錯誤檢查問題)
- 測試邏輯、xargs 和參數替換
- bash函數
- 陣列、for 迴圈、測試和 &&
printf
和參數替換解決方案該函數的一個鮮為人知的特點
printf
是,如果您調用printf "%c" someString
,它只會列印第一的該字串的字元。因此,我們可以避免使用帶有參數擴展的 case 語句,printf
如下所示:xieerqi:$ cat someScript.sh #!/bin/bash find /etc -type $(printf "%c" $1 )
現在執行:
xieerqi:$ sudo ./someScript.sh directory | head [sudo] password for xieerqi: /etc /etc/logrotate.d /etc/apm /etc/apm/event.d /etc/apm/scripts.d /etc/apm/resume.d /etc/apm/suspend.d /etc/speech-dispatcher /etc/speech-dispatcher/modules /etc/speech-dispatcher/clients
這裡的一個限制是我們分叉一個進程來調用
printf
,函數解決方案避免了這一點 - 函數和 case 結構都是本機 bash 工具。xargs
和參數替換使用 bash 的參數替換,我們可以從變數中截取子字串(例如
${VAR:0:3}
給出 的前 3 個字元VAR
);在這種情況下,我們想要類型的第一個字元directory
或file
。然後我們可以xargs
將其作為參數傳遞給find
echo ${1:0:1} | xargs -I {} find /etc -type {}
手冊
find
頁提到,對於-type
Solaris 上的標誌,有一種稱為「門檔案」的東西,用大寫字母 表示D
,但由於我們使用的是 Linux,所以我們可以說這是一個可以合理忽略的限制。然而,這段程式碼還有另一個危險 - 如果用戶輸入
flower
作為$1
參數,這仍然會搜索-type f
,因為我們採用任何字串的第一個字元......換句話說,沒有錯誤檢查。find
帶參數擴展進一步擴展參數,我們可以這樣做:
find /etc -type ${1:0:1}
基本上,是一個帶有
find
命令和變數子字串的單行程式碼$1
。另外,沒有錯誤檢查。失敗案例結構
最後三種方法的最大問題是錯誤檢查。當您相信用戶不是假人,或只是為自己編寫程式碼時,它們就很好。現在,在 Java 中,如果您只是省略該命令,則可以編寫一條
switch
語句,該語句將在多種情況下執行相同的命令break
。終結者bash
也可以做到這一點;&
。引用自man bash
使用
;&
in 代替;;
會導致執行繼續與下一組模式關聯的清單。我們要做的就是測試類型,例如「目錄」、「檔案」、「區塊」等,然後使用參數替換來截掉第一個字元。像這樣
#!/bin/bash case "$1" in "directory") ;& "file");& "block") find /etc -type ${1:0:1} ;; *) exit 1 ;; esac
5.測試邏輯、xargs 和參數替換
Basic idea here is that we're sending $1 variable through pipe to `xargs`, which in turn substitutes it into test (again square brackets). `xargs` in turn builds the actual command that runs by replacing `{}` with whatever was passed to `xargs`. As for test command, it's simple or statement, `EXPRESSION -o EXPRESSION ` , where we test if string $1 is equal to either "file" or "directory string"
echo "$1" | xargs -I {} [ "{}" = "file" -o "{}" = "directory" ] \
&& find /etc -type ${1:0:1}
`xargs` is really useful when you need to process multiple argumens with the same command. Considering that in this case we only have one argument that needs to be processed with the same command , this can be simplified to
[ "$1" = "file" -o "$1" = "directory" ] && find /etc -type ${1:0:1}
Of course the big limitation is that if you test for more than one type, you need longer `[ EXPR1 -o EXPR2 ]` structure with multiple `-o` parts.
功能解決方案
find
命令可以放入函數中,然後可以使用位置參數呼叫函數。例如:
function findStuff { find /etc -type "$1" }
這是一個小演示。請注意,我使用的是
sudo
因為普通用戶的許多檔案/etc
沒有讀取權限xieerqi:$ sudo ./someScript.sh directory | head [sudo] password for xieerqi: /etc /etc/logrotate.d /etc/apm /etc/apm/event.d /etc/apm/scripts.d /etc/apm/resume.d /etc/apm/suspend.d /etc/speech-dispatcher /etc/speech-dispatcher/modules /etc/speech-dispatcher/clients xieerqi:$ sudo ./someScript.sh file | head [sudo] password for xieerqi: /etc/hosts.deny /etc/logrotate.d/speech-dispatcher /etc/logrotate.d/pm-utils /etc/logrotate.d/rsyslog /etc/logrotate.d/yate /etc/logrotate.d/apport /etc/logrotate.d/apt /etc/logrotate.d/consolekit /etc/logrotate.d/fail2ban /etc/logrotate.d/cups-daemon xieerqi:$ cat someScript.sh #!/bin/bash function findStuff { find /etc -type "$1" } case "$1" in "directory")findStuff d ;; "file") findStuff f;; esac
陣列、for 迴圈、測試和 &&
這裡的基本想法 - 將使用者的輸入與清單進行匹配,如果匹配則執行某些操作。我們建立一個要檢查的項目數組,設定一個測試條件(方括號是
test
指令的別名),然後執行一個迴圈來測試 $1 變數。&&
當且僅當左側的內容&&
成功時,運算子才允許執行指令。因此,如果我們在數組中找到一個字串,我們就會執行 find 命令。 ${1:0:1} 在前面的範例中討論過 - 參數擴充功能會從我們符合的類型中刪除第一個字元。因此,此解決方案具有錯誤檢查功能,並將全部程式碼打包為僅 3 行(如果包含#!
行,則為 4 行)。#!/bin/bash array=("file" "directory" "block"); for TYPE in "${array[@]}"; do [ "$1" = "$TYPE" ] && find /etc/ -type ${1:0:1}; done
答案2
將其放入函數中:
MyFind () {
COMMAND="find /etc -type "
case $1:
"directory") $COMMAND d
;;
esac
}
現在您可以隨時將其用作MyFind $TYPE
關於您的第一則評論
您也可以只將 case 語句放入函數中
FType () {
case $1 in
"directory") echo d;;
"file") echo f;;
esac
}
COMMAND="find /etc -type "
$COMMAND $(FType $TYPE)
答案3
[[ "$1" == "directory" ]] && find /etc -type d
答案4
這只是 Serg 出色答案中已經廣泛的選項的另一個想法。
您也許可以使用 analias
來實現此目的 - 可能會對您目前的工作方式進行一些細微的更改。
Analias
只是您選擇映射到更大字串的單詞,shell 每當遇到它時都會擴展該字串。也許最常見的用例是將“預設值”應用於現有命令,如下所示:
alias ls='ls -a --color=auto'
但是,不要求您alias
必須以現有命令命名 - 或甚至比其目標模式短 - 所以您可以例如alias tgzcreator='tar -czvf'
alias
es 與定義它們的 shell 共享生命週期。alias
可以為您設定“持久” ~/.bash_aliases
,其中應該.bashrc
由大多數編寫良好的預設腳本等自動取得。
請注意一些提示:
- 您不必擔心特定腳本中先前定義的內容是否
alias
會幹擾您的程式碼,而是可以在其前面加上反斜線以確保alias
跳過 ing。例如,我通常alias
cp
會cp -i
避免意外覆蓋某些內容,但在某些腳本中,我顯然想覆蓋一些東西。 (我不會使用一些可怕的解決方法,例如設定已知的未alias
編輯用戶!)因此,在該腳本中,我將使用\cp src dst
alias
預設情況下,es 可能不會在 shell 腳本中獲取,這些腳本會啟動它們自己的 shell 的非互動式副本。您可以透過expand_aliases
在腳本中設定選項來確保它們擴展。我從以下地方得到這個:https://stackoverflow.com/questions/2197461/how-to-set-an-alias-inside-a-bash-shell-script-so-that-is-it-visible-from-the-ou
因此,根據您帖子中可用的有限上下文,您可能想做一些事情有點像這樣:
shopt -s expand_aliases
alias get_etc_dir='find /etc -type d'
alias get_etc_fil='find /etc -type f'
對於您來說,如果不進行調整,這可能無法運作,例如將參數化 inode 類型變更為每個別名後綴。這只是用戶如何縮短一般代碼位的另一種選擇。無論哪種方式,我都試圖根據我所知道的進行全面解釋,希望它在某些地方有用。
(另外,我建議將其移至 Unix/Linux SE,假設這對於非 Ubuntu 特定的事物來說是理想的選擇?)