如何正确使用换行符作为分隔符将输入保存到数组中
how to correctly use newline as delimiter for saving input into array
我正在编写一个 bash 脚本,该脚本应该备份用户可以指定的文件夹。脚本纯属娱乐,进入bash。我不会将它用于真正的备份目的。
这个想法是用户可以输入他们想要更新的特定文件夹。每次输入后,他们会点击 ENTER
,然后在控制台中输入另一个目录。完成后,他们会按 CTRL-D
。然后输入将保存到数组 USER_DIR
中。然后将检查该数组以查看目录是否存在。如果它们确实存在,条目将保持不变。如果它们不存在,条目将被覆盖为默认值 /home/$USER
.
我在 AskUbuntu 上发现了一个有趣的想法(我正在使用 Ubuntu):
while read line
do
my_array=("${my_array[@]}" $line)
done
echo ${my_array[@]}
我用过它并尝试输入多个目录,但只有当输入恰好是一个目录时它才有效。每次我使用多个目录作为输入时,它默认为/home/$USER
。我已将 echo ${my_array[@]}
放在脚本的不同位置,看起来问题出在分隔符上,因为输入似乎与空格连接在一起。
我试图寻找一种方法来遍历数组并在保存输入后更改分隔符。我在 Whosebug:
上发现了一个有趣的想法
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
names=($names) # split to array $names
IFS=$SAVEIFS # Restore IFS
我试过了,但它也只接受单数输入。我的脚本目前看起来像这样:
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
while read line
do
USER_DIR=("${USER_DIR[@]}" "$line")
done
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
# check if user input in array is valid (directory exists)
for ((i=0; i<${#USER_DIR[@]}; i++))
do
if [[ -d "${USER_DIR[$i]}" ]]; then
# directory exists
USER_DIR=("${USER_DIR[@]}")
echo "${USER_DIR[@]}" # for debugging
else
# directory does not exist
USER_DIR=("/home/$USER")
echo "${USER_DIR[@]}" # for debugging
fi
done
# show content of array
echo "Backups of the following directories will be done:"
for i in "${USER_DIR[@]}"; do echo "$i"; done
如有任何帮助,我们将不胜感激。感谢您的时间。
您不需要此代码段。
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
默认读取命令使用\n
作为输入分隔符。
演示:
$./test.ksh
Please enter absolute path of directory for backup. Default is /home/renegade
You can choose multiple directories. Press CTRL-D to start backup.
abc
123
def
Backups of the following directories will be done:
abc
123
def
$cat test.ksh
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
while read line
do
USER_DIR=( "${USER_DIR[@]}" "$line" )
done
#SAVEIFS=$IFS # Save current IFS
#IFS=$'\n' # Change IFS to new line
#USER_DIR=($USER_DIR) # split to array $names
#IFS=$SAVEIFS # Restore IFS
# show content of array
echo "Backups of the following directories will be done:"
for i in "${USER_DIR[@]}"; do echo "$i"; done
$
这里有几个严重的问题,我还有一些小的建议。第一个(可能)是次要的:你的脚本没有以 shebang 行开头来告诉系统它是用什么脚本语言编写的。你正在使用 bash 功能(不仅仅是基本的 POSIX shell) 所以你需要从一个 shebang 开始,它说 运行 bash,而不是一些通用的 shell。所以第一行应该是 #!/bin/bash
或 #!/usr/bin/env bash
.
接下来,让我们看一下 while read
循环:
while read line
do
USER_DIR=("${USER_DIR[@]}" "$line")
done
这个可以正常工作。它用用户的输入填充数组,每个条目一个元素。听起来您使用 echo ${USER_DIR[@]}
检查结果数组,并感到困惑,以为出了什么问题;但是循环工作正常,是 echo ${USER_DIR[@]}
具有误导性。正如我在评论中所说,请改用 declare -p USER_DIR
,bash 将打印一条语句(如果您要 运行 它)将重新创建数组的当前内容。事实证明,这是一种很好的、通常明确的显示数组内容的方法。
我确实有一些小建议:使用小写或混合大小写的变量名称(例如 user_dir
、userDir
或类似名称)而不是全部大写的名称。有一堆具有特殊含义的全大写名称,如果您使用全大写命名,很容易不小心使用其中一个并造成混乱。此外,我会在使用 user_dir=()
循环之前将数组初始化为空,以确保它开始时为空。正如 Jetchisel 在评论中所说, user_dir+=("$line")
将是向数组添加新元素的更简单方法。只是不要省略括号,否则它将附加到第一个元素而不是添加新元素。
好的,下一节:
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
这似乎是在尝试解决一个不存在的问题,但最终会在此过程中产生一个新问题。正如我所说,USER_DIR
已经作为(正确填充的)数组存在,因此 $USER_DIR
(没有 [@]
或任何东西)只是获取它的第一个元素。这意味着 USER_DIR=($USER_DIR)
只获取第一个元素,尝试在换行符上拆分它(因为那是 IFS
设置的内容),但是没有任何元素,所以什么也没有发生。然后它将 USER_DIR
设置为由 组成的数组,只是第一个元素 .
本质上,它是从数组中删除除第一个元素以外的所有元素。只需删除整个部分即可。
好的,下一个:
for ((i=0; i<${#USER_DIR[@]}; i++))
这在 bash 中适用于具有默认索引的数组(这是您拥有的),但我个人的偏好是改用它:
for i in "${!USER_DIR[@]}"
看到里面的 !
了吗?这使得它获得数组中 索引值 的列表。在 bash 中工作,在 zsh 中工作(它使用不同的编号约定),我不必试图记住哪个使用哪个约定。它甚至适用于关联数组!但这也没什么大不了的,只是我的喜好。
现在,在循环内:
if [[ -d "${USER_DIR[$i]}" ]]; then
# directory exists
USER_DIR=("${USER_DIR[@]}")
这扩展了数组的内容(正确引用和所有内容,这样它就不会弄乱),并将它重新赋值给数组。它绝对没有任何作用!请注意,这里没有什么是正确的,但是有更简单的方法可以什么都不做。我会完全删除它,只使用 if [[ ! -d "${USER_DIR[$i]}" ]]; then
后跟 "what do do if it doesn't exist" 部分。说到这里:
else
# directory does not exist
USER_DIR=("/home/$USER")
这是第二个严重的问题。它将 整个数组 替换为指向您的主目录的单个条目。您只想替换 当前条目并保留数组的其余部分,因此请使用 USER_DIR[$i]="/home/$USER"
。请注意,数组引用周围没有 ${ }
—— 那些是当您 从数组中获取 一个值时使用的,而不是当您 设置时使用的一个。
好的,还有一个建议:与其将所有值读入一个数组,然后返回遍历它们以修复不存在的值,不如在读取目录路径时进行存在性测试,然后首先将正确的东西添加到数组中?您甚至可以在用户输入名称时给予反馈,如下所示:
#!/bin/bash
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
user_dir=()
while read line
do
if [[ -d "$line" ]]; then
user_dir+=("$line")
else
echo "$line isn't an existing directory; we'll back up /home/$USER instead"
user_dir+=("/home/$USER")
fi
done
# show content of array
echo "Backups of the following directories will be done:"
for i in "${user_dir[@]}"; do echo "$i"; done
我正在编写一个 bash 脚本,该脚本应该备份用户可以指定的文件夹。脚本纯属娱乐,进入bash。我不会将它用于真正的备份目的。
这个想法是用户可以输入他们想要更新的特定文件夹。每次输入后,他们会点击 ENTER
,然后在控制台中输入另一个目录。完成后,他们会按 CTRL-D
。然后输入将保存到数组 USER_DIR
中。然后将检查该数组以查看目录是否存在。如果它们确实存在,条目将保持不变。如果它们不存在,条目将被覆盖为默认值 /home/$USER
.
我在 AskUbuntu 上发现了一个有趣的想法(我正在使用 Ubuntu):
while read line
do
my_array=("${my_array[@]}" $line)
done
echo ${my_array[@]}
我用过它并尝试输入多个目录,但只有当输入恰好是一个目录时它才有效。每次我使用多个目录作为输入时,它默认为/home/$USER
。我已将 echo ${my_array[@]}
放在脚本的不同位置,看起来问题出在分隔符上,因为输入似乎与空格连接在一起。
我试图寻找一种方法来遍历数组并在保存输入后更改分隔符。我在 Whosebug:
上发现了一个有趣的想法SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
names=($names) # split to array $names
IFS=$SAVEIFS # Restore IFS
我试过了,但它也只接受单数输入。我的脚本目前看起来像这样:
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
while read line
do
USER_DIR=("${USER_DIR[@]}" "$line")
done
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
# check if user input in array is valid (directory exists)
for ((i=0; i<${#USER_DIR[@]}; i++))
do
if [[ -d "${USER_DIR[$i]}" ]]; then
# directory exists
USER_DIR=("${USER_DIR[@]}")
echo "${USER_DIR[@]}" # for debugging
else
# directory does not exist
USER_DIR=("/home/$USER")
echo "${USER_DIR[@]}" # for debugging
fi
done
# show content of array
echo "Backups of the following directories will be done:"
for i in "${USER_DIR[@]}"; do echo "$i"; done
如有任何帮助,我们将不胜感激。感谢您的时间。
您不需要此代码段。
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
默认读取命令使用\n
作为输入分隔符。
演示:
$./test.ksh
Please enter absolute path of directory for backup. Default is /home/renegade
You can choose multiple directories. Press CTRL-D to start backup.
abc
123
def
Backups of the following directories will be done:
abc
123
def
$cat test.ksh
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
while read line
do
USER_DIR=( "${USER_DIR[@]}" "$line" )
done
#SAVEIFS=$IFS # Save current IFS
#IFS=$'\n' # Change IFS to new line
#USER_DIR=($USER_DIR) # split to array $names
#IFS=$SAVEIFS # Restore IFS
# show content of array
echo "Backups of the following directories will be done:"
for i in "${USER_DIR[@]}"; do echo "$i"; done
$
这里有几个严重的问题,我还有一些小的建议。第一个(可能)是次要的:你的脚本没有以 shebang 行开头来告诉系统它是用什么脚本语言编写的。你正在使用 bash 功能(不仅仅是基本的 POSIX shell) 所以你需要从一个 shebang 开始,它说 运行 bash,而不是一些通用的 shell。所以第一行应该是 #!/bin/bash
或 #!/usr/bin/env bash
.
接下来,让我们看一下 while read
循环:
while read line
do
USER_DIR=("${USER_DIR[@]}" "$line")
done
这个可以正常工作。它用用户的输入填充数组,每个条目一个元素。听起来您使用 echo ${USER_DIR[@]}
检查结果数组,并感到困惑,以为出了什么问题;但是循环工作正常,是 echo ${USER_DIR[@]}
具有误导性。正如我在评论中所说,请改用 declare -p USER_DIR
,bash 将打印一条语句(如果您要 运行 它)将重新创建数组的当前内容。事实证明,这是一种很好的、通常明确的显示数组内容的方法。
我确实有一些小建议:使用小写或混合大小写的变量名称(例如 user_dir
、userDir
或类似名称)而不是全部大写的名称。有一堆具有特殊含义的全大写名称,如果您使用全大写命名,很容易不小心使用其中一个并造成混乱。此外,我会在使用 user_dir=()
循环之前将数组初始化为空,以确保它开始时为空。正如 Jetchisel 在评论中所说, user_dir+=("$line")
将是向数组添加新元素的更简单方法。只是不要省略括号,否则它将附加到第一个元素而不是添加新元素。
好的,下一节:
SAVEIFS=$IFS # Save current IFS
IFS=$'\n' # Change IFS to new line
USER_DIR=($USER_DIR) # split to array $names
IFS=$SAVEIFS # Restore IFS
这似乎是在尝试解决一个不存在的问题,但最终会在此过程中产生一个新问题。正如我所说,USER_DIR
已经作为(正确填充的)数组存在,因此 $USER_DIR
(没有 [@]
或任何东西)只是获取它的第一个元素。这意味着 USER_DIR=($USER_DIR)
只获取第一个元素,尝试在换行符上拆分它(因为那是 IFS
设置的内容),但是没有任何元素,所以什么也没有发生。然后它将 USER_DIR
设置为由 组成的数组,只是第一个元素 .
本质上,它是从数组中删除除第一个元素以外的所有元素。只需删除整个部分即可。
好的,下一个:
for ((i=0; i<${#USER_DIR[@]}; i++))
这在 bash 中适用于具有默认索引的数组(这是您拥有的),但我个人的偏好是改用它:
for i in "${!USER_DIR[@]}"
看到里面的 !
了吗?这使得它获得数组中 索引值 的列表。在 bash 中工作,在 zsh 中工作(它使用不同的编号约定),我不必试图记住哪个使用哪个约定。它甚至适用于关联数组!但这也没什么大不了的,只是我的喜好。
现在,在循环内:
if [[ -d "${USER_DIR[$i]}" ]]; then
# directory exists
USER_DIR=("${USER_DIR[@]}")
这扩展了数组的内容(正确引用和所有内容,这样它就不会弄乱),并将它重新赋值给数组。它绝对没有任何作用!请注意,这里没有什么是正确的,但是有更简单的方法可以什么都不做。我会完全删除它,只使用 if [[ ! -d "${USER_DIR[$i]}" ]]; then
后跟 "what do do if it doesn't exist" 部分。说到这里:
else
# directory does not exist
USER_DIR=("/home/$USER")
这是第二个严重的问题。它将 整个数组 替换为指向您的主目录的单个条目。您只想替换 当前条目并保留数组的其余部分,因此请使用 USER_DIR[$i]="/home/$USER"
。请注意,数组引用周围没有 ${ }
—— 那些是当您 从数组中获取 一个值时使用的,而不是当您 设置时使用的一个。
好的,还有一个建议:与其将所有值读入一个数组,然后返回遍历它们以修复不存在的值,不如在读取目录路径时进行存在性测试,然后首先将正确的东西添加到数组中?您甚至可以在用户输入名称时给予反馈,如下所示:
#!/bin/bash
echo "Please enter absolute path of directory for backup. Default is ""/home/$USER"
echo "You can choose multiple directories. Press CTRL-D to start backup."
# save user input into array
user_dir=()
while read line
do
if [[ -d "$line" ]]; then
user_dir+=("$line")
else
echo "$line isn't an existing directory; we'll back up /home/$USER instead"
user_dir+=("/home/$USER")
fi
done
# show content of array
echo "Backups of the following directories will be done:"
for i in "${user_dir[@]}"; do echo "$i"; done