Bash 从多个变量中获取变量的快捷方式
Bash shortcut for take the variable that exists from many
这个问题我已经有一段时间了。在我使用 bash 脚本的工作中,有很多情况我必须将文件路径保存到变量以供以后的命令使用。我们使用后缀为 T1w、T2w 和掩码的文件,在许多情况下我们有一个优先文件(例如 T1ws),但如果没有,我们将使用另一个文件(例如 T2w)。我通常使用以下 bash 语法来搜索适当的文件并将其保存到变量中(假设我在当前目录中搜索):
if [ -f xxx_T1w ]; then
var1=xxx_T1w
elif [ -f xxx_T2w ]; then
var1=xxx_T2w
fi
很好,它可以工作,但我一直很好奇 bash 中是否有一种方法可以编写如下代码: 如果两个给定文件之一存在,则取一个存在的文件, 如果两者都存在,取第一个文件。
我对伪代码很在行,但可能是这样的:
if [ -f file_T1w ] || [ -f file_T2w ]; then
var1=existing file
fi
我经常重复使用此代码,有时 elif
语句会变得很长,如果有更长的优先文件列表我们可以遍历,我一直想知道是否有更多紧凑的方式来做到这一点。
使用这个 Perl one-liner(它使用 1 个或多个文件作为参数):
var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w xxx_T2w )
示例(请注意,如果列出的文件中有 none 个存在,则 $var1
将为空):
$ rm xxx_T1w
$ touch xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w xxx_T2w )
$ echo ${var1}
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T2w xxx_T1w )
$ echo ${var1}
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w )
$ echo ${var1}
$
Perl one-liner 使用这些命令行标志:
-e
:告诉 Perl 查找代码 in-line,而不是在文件中。
-l
: 在执行代码 in-line 之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"
),并在打印时附加它。
-MList::Util='first'
: 使用模块 List::Util
,并从中导入子程序 first
。这个标准库模块包含在您的 Perl 发行版中(无需额外安装)。
@ARGV
:命令行参数数组(此处为文件)。
print first { -f $_ } @ARGV;
:打印在命令行上作为参数列出的第一个文件,该文件存在并且是文件(不是目录)。
另见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches
perldoc perlvar
: Perl predefined variables
List::Util::first
我能想到的最有希望的是循环:
for f in file1 file2; do
if [ -f "$f" ]; then
var="$f"
break
fi
done
您可以根据需要使用 &&
来缩短代码,但要以支持 errexit
shell 选项为代价。对于只有两个文件,这种方法的好处是值得怀疑的,因为它与您已经编写的 if
/elif
语句的代码量大致相同,而且它试图做什么可能不太清楚.但对于许多文件来说,这将更加简洁。
正如其他人所建议的那样,我认为将其封装在 shell 函数中绝对是一件好事。请记住,变量赋值不是(必须)作用于 bash 中的 shell 函数,因此您可以毫无问题地从函数内部设置变量并在其他地方使用它;但是,如果对您更有用,您也可以考虑让函数将找到的文件的名称打印到标准输出。
如果你把它放在一个函数中,你实际上可以使用 shell 的 built-in 循环位置参数的能力,就像这样:
find_file() {
for f; do
if [ -f "$f" ]; then
var="$f"
return
fi
done
}
Bash 函数 return 仅状态代码,因此没有 return 值可分配给变量,就像您在使用标准编程语言时所做的那样,例如 var = get_file(file1 file2)
.
但是你可以定义一个函数,有这个方便的方法
get_file var file1 file2 file3
这是您可以在脚本中使用的简短可重用代码。
修改@David Z的,你的函数可以参考
第一个参数的名称,在 shift 之后,迭代其余参数以测试它们。这里有一个陷阱,参见 this,你必须避免循环名称引用,我会选择一个难以在你的其余脚本中存在的变量名。
function get_file() {
declare -n get_file_var=
shift
for f; do
if [ -f "$f" ]; then
get_file_var="$f"
return 0
fi
done
return 1
}
return 状态可用于确定是否发生任何分配。
get_file var file1 file2 file3
echo $?
echo $var
或者这个:
if get_file var file1 file2; then
echo var was assigned $var
else
echo no file assigned # var can be empty string or keep its previous value
fi
你也可以用数组来调用它:
arr=("missing filename" file1 file2)
get_file x "${arr[@]}"
这个问题我已经有一段时间了。在我使用 bash 脚本的工作中,有很多情况我必须将文件路径保存到变量以供以后的命令使用。我们使用后缀为 T1w、T2w 和掩码的文件,在许多情况下我们有一个优先文件(例如 T1ws),但如果没有,我们将使用另一个文件(例如 T2w)。我通常使用以下 bash 语法来搜索适当的文件并将其保存到变量中(假设我在当前目录中搜索):
if [ -f xxx_T1w ]; then
var1=xxx_T1w
elif [ -f xxx_T2w ]; then
var1=xxx_T2w
fi
很好,它可以工作,但我一直很好奇 bash 中是否有一种方法可以编写如下代码: 如果两个给定文件之一存在,则取一个存在的文件, 如果两者都存在,取第一个文件。 我对伪代码很在行,但可能是这样的:
if [ -f file_T1w ] || [ -f file_T2w ]; then
var1=existing file
fi
我经常重复使用此代码,有时 elif
语句会变得很长,如果有更长的优先文件列表我们可以遍历,我一直想知道是否有更多紧凑的方式来做到这一点。
使用这个 Perl one-liner(它使用 1 个或多个文件作为参数):
var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w xxx_T2w )
示例(请注意,如果列出的文件中有 none 个存在,则 $var1
将为空):
$ rm xxx_T1w
$ touch xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w xxx_T2w )
$ echo ${var1}
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T2w xxx_T1w )
$ echo ${var1}
xxx_T2w
$ var1=$( perl -MList::Util='first' -le 'print first { -f $_ } @ARGV;' xxx_T1w )
$ echo ${var1}
$
Perl one-liner 使用这些命令行标志:
-e
:告诉 Perl 查找代码 in-line,而不是在文件中。
-l
: 在执行代码 in-line 之前去除输入行分隔符(默认情况下在 *NIX 上为 "\n"
),并在打印时附加它。
-MList::Util='first'
: 使用模块 List::Util
,并从中导入子程序 first
。这个标准库模块包含在您的 Perl 发行版中(无需额外安装)。
@ARGV
:命令行参数数组(此处为文件)。
print first { -f $_ } @ARGV;
:打印在命令行上作为参数列出的第一个文件,该文件存在并且是文件(不是目录)。
另见:
perldoc perlrun
: how to execute the Perl interpreter: command line switches
perldoc perlvar
: Perl predefined variables
List::Util::first
我能想到的最有希望的是循环:
for f in file1 file2; do
if [ -f "$f" ]; then
var="$f"
break
fi
done
您可以根据需要使用 &&
来缩短代码,但要以支持 errexit
shell 选项为代价。对于只有两个文件,这种方法的好处是值得怀疑的,因为它与您已经编写的 if
/elif
语句的代码量大致相同,而且它试图做什么可能不太清楚.但对于许多文件来说,这将更加简洁。
正如其他人所建议的那样,我认为将其封装在 shell 函数中绝对是一件好事。请记住,变量赋值不是(必须)作用于 bash 中的 shell 函数,因此您可以毫无问题地从函数内部设置变量并在其他地方使用它;但是,如果对您更有用,您也可以考虑让函数将找到的文件的名称打印到标准输出。
如果你把它放在一个函数中,你实际上可以使用 shell 的 built-in 循环位置参数的能力,就像这样:
find_file() {
for f; do
if [ -f "$f" ]; then
var="$f"
return
fi
done
}
Bash 函数 return 仅状态代码,因此没有 return 值可分配给变量,就像您在使用标准编程语言时所做的那样,例如 var = get_file(file1 file2)
.
但是你可以定义一个函数,有这个方便的方法
get_file var file1 file2 file3
这是您可以在脚本中使用的简短可重用代码。
修改@David Z的
function get_file() {
declare -n get_file_var=
shift
for f; do
if [ -f "$f" ]; then
get_file_var="$f"
return 0
fi
done
return 1
}
return 状态可用于确定是否发生任何分配。
get_file var file1 file2 file3
echo $?
echo $var
或者这个:
if get_file var file1 file2; then
echo var was assigned $var
else
echo no file assigned # var can be empty string or keep its previous value
fi
你也可以用数组来调用它:
arr=("missing filename" file1 file2)
get_file x "${arr[@]}"