無需重複自己即可編寫空執行選項的正確方法?
我正在編寫一個腳本,用於搜尋遠端伺服器上的文件並將它們傳輸回我的本地電腦。我希望能夠先進行試執行,所以我知道要帶回哪些文件。
我目前正在使用我在這裡
getopts
找到的一些程式碼的混合和輸出重定向。在我看來,通過我的研究,從 ZSH 或 Bash 函式返回數組是不切實際的。對我來說,這讓我很難理解如何編寫這個腳本而不必重複自己。
這是我目前的腳本:
**編輯:**請原諒我將一些 bashisms 與 zsh 混合在一起,我開始使用
#!/bin/bash
但切換到 zsh 編寫此腳本。#!/usr/local/bin/zsh RED='\033[0;31m' NC='\033[0m' GREEN='\033[0;32m' YELLOW='\033[0;33m' dry_run=0 yesterday=1 # Establish -n flag means to do a dry run. while getopts "ny:" flag; do case "$flag" in n) dry_run=1 ;; y) yesterday=${OPTARG} ;; *) echo 'error in command line parsing' >&2 exit 1 esac done shift $(($OPTIND-1)) # This is the folder I'm interested in getting files from folder=${1:?"You must define a folder of interest"} # Check to see if dry-run, if not proceed with copying the files over. if [ "$dry_run" -eq 1 ]; then print -Pn "\n%S%11F%{Initiating Dry-Run%}%s%f" # SSH onto server and find the most recently updated folder. # Then place that newest folder and folder of interest into the absolute file path. # Then SSH again and use find with that file-path # Return array of file paths # TODO: **THIS IS THE SECTION I NEED TO REFACTOR INTO A FUNCTION** bison_remote_files=($( { { bison_latest_run=$(ssh -qn falcon1 'find /projects/bison/git/* -mindepth 0 -maxdepth 0 -type d -printf "%T@\t%f\n"' | sort -t$'\t' -r -nk1,5 | sed -n "$yesterday"p | cut -f2-) bison_remote_path=$( echo $bison_latest_run | awk -v folder="$folder" '{print "/projects/bison/git/"$1"/assessment/LWR/validation/"folder}') ssh -qn falcon1 \ "find $bison_remote_path -type f -name '*_out.csv' -not -path '*/doc/*' 2>/dev/null" >&3 3>&-; echo "$?" print -Pn "\n\n%U%B%13F%{Fetching data from:%}%u %B%12F%{ /projects/bison/git/${bison_latest_run}%}%b%f\n" >&2 } | { until read -t1 ret; do print -Pn "%S%11F%{.%}%s%f" >&2 done exit "$ret" } } 3>&1)) # Maninpulate remote file paths to match the local machine directory local_file_path=($(for i in "${bison_remote_files[@]}"; do echo $i | gsed -E "s|/projects/bison/git/bison_[0-9]{8}|$HOME/Documents/projects/bison|g" done )) # Loop through remote and local and show where they will be placed for ((i=1; i<=${#bison_remote_files[@]}; i++)); do print -P "\u251C\U2500%B%1F%{Remote File ->%}%b%f ${bison_remote_files[i]}" print -P "\u251C\U2500%B%10F%{Local File ->%}%b%f ${local_file_path[i]}" if [[ $i -lt ${#bison_remote_files[@]} ]]; then print -Pn "\U2502\n" else print -Pn "\U2514\U2500\U2504\U27E2\n" fi done # If it's not a dry run, grab all the files using scp # This is the part I am stuck... # All my defined variables are un-run in the scope above # How do I craft a function (or something else) so I don't have to do all the above all over again? else printf "${YELLOW}Fetching Data from ${NC}(${GREEN}${bison_latest_run}${NC})${YELLOW}...${NC}\n" for ((i=0; i<${#NEW_RFILEP[@]}; i++)); do scp -qp mcdodyla@falcon1:"${NEW_RFILEP[i]}" "${LOCAL_FILEP[i]}" # Check if scp was successful, if it was show green. if [ ${PIPESTATUS[0]} -eq 0 ]; then printf "${GREEN}File Created/Updated at:${NC} ${LOCAL_FILEP[i]}\n" else printf "${RED}Error Fetching File:${NC} ${NEW_RFILEP[i]}\n" fi done printf "${YELLOW}Bison Remote Fetch Complete!${NC}\n" fi
如您所見,我的所有數據都卡在第一個 if 語句案例中,因此如果我不想進行空執行,那麼我必須再次執行所有程式碼。由於 bash/zsh 並沒有真正返回數組,我該如何重構這段程式碼?
**編輯:**這是一個範例案例:
> bfetch -n "HBEP" Initiating Dry-Run... Fetching data from: /projects/bison/git/bison_20190827 ├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK370/HBEP_BK370_out.csv ├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK370/HBEP_BK370_out.csv │ ├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK363/HBEP_BK363_out.csv ├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK363/HBEP_BK363_out.csv │ ├─Remote File -> /projects/bison/git/bison_20190827/assessment/LWR/validation/HBEP/analysis/BK365/HBEP_BK365_out.csv ├─Local File -> /Users/mcdodj/Documents/projects/bison/assessment/LWR/validation/HBEP/analysis/BK365/HBEP_BK365_out.csv
我不知道
zsh
,但是:
- 首先確保您所有的“對話式”列印語句都轉到
stderr
,而不是stdout
,例如:print -Pn "\n%S%11F%{Initiating Dry-Run%}%s%f" >&2
和許多其他人。
2)而不是執行你的
scp
語句,printf
他們到stdout
,例如:printf 'scp -qp mcdodyla@falcon1:"%s" "%s"\n' "${NEW_RFILEP[i]}" "${LOCAL_FILEP[i]}"
這適用於所有修改文件系統的語句,例如
cp
,rm
,rsync
,mkdir
,touch
, 等等。通過對您的腳本的簡要檢查,scp
這是唯一一個讓我大吃一驚的,但您比我更了解您的程式碼。再次檢查您的程式碼,並三重檢查所有 fs 修改(“不可逆”)命令是否都轉換為
printf
’s. 你不想錯過任何一個。現在,只是為了測試您是否正確轉換了腳本,執行它,然後扔掉
stderr
:./myscript 2>/dev/null
那應該只顯示
stdout
來自您的腳本。您必須確保所有輸出都是有效的 shell 語法。 所有資訊性消息都應該發送到
stderr
,並且所有“操作”語句都應該printf
發送到stdout
。如果您仍然有一些資訊性消息洩漏到stdout
中,請返回並再次編輯您的腳本並確保重定向列印語句>&2
。一旦您明確證明您已將資訊消息發送到
stderr
,並且實際工作將發送到stdout
,您的轉換就完成了。要空執行,只需執行腳本:
./myscript
要實際執行這項工作,請再次執行腳本並通過管道
stdout
傳輸到 shell:./myscript | zsh -v