我正在编写一个脚本,用于搜索远程服务器上的文件并将它们传输回我的本地计算机。我希望能够先进行试运行,所以我知道要带回哪些文件。
我目前正在使用我在这里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
,但是:1) 首先确保您所有的“对话式”打印语句都转到
stderr
,而不是stdout
,例如:和许多其他人。
2)而不是执行你的
scp
语句,printf
他们到stdout
,例如:这适用于所有修改文件系统的语句,例如
cp
,rm
,rsync
,mkdir
,touch
, 等等。通过对您的脚本的简要检查,scp
这是唯一一个让我大吃一惊的,但您比我更了解您的代码。再次检查您的代码,并三重检查所有 fs 修改(“不可逆”)命令是否都转换为
printf
's. 你不想错过任何一个。现在,只是为了测试您是否正确转换了脚本,运行它,然后扔掉
stderr
:./myscript 2>/dev/null
那应该只显示
stdout
来自您的脚本。您必须确保所有输出都是有效的 shell 语法。 所有信息性消息都应该发送到
stderr
,并且所有“操作”语句都应该printf
发送到stdout
。如果您仍然有一些信息性消息泄漏stdout
,请返回并再次编辑您的脚本,并确保打印语句被重定向>&2
。一旦您明确证明您已将信息消息发送到
stderr
,并且实际工作将发送到stdout
,您的转换就完成了。要空运行,只需运行脚本:
要实际执行这项工作,请再次运行脚本并通过管道
stdout
传输到 shell:我对脚本的了解还不够好,无法知道不同的 shell 能做什么。
但是,如果最终结果是您必须再次重复所有代码,您可以在像 m4 这样的宏处理器中构建您的代码 - 并使用 m4 处理器将源代码扩展为完整的脚本。
例如,如果用汇编语言编写,其中没有数组,但必须在地址中迭代循环,则将编写一次例程,其中包含一些用于固定地址的宏变量,并且在宏文件中,定义 ' array' 和一个 for 循环 - 之后由 m4 处理,一个人将拥有完整的重复源。
也许你可以在这里做类似的事情?或者也许是一个毫无价值的想法。只是一个想法。