如何正确使用换行符作为将输入保存到数组的定界符

问题描述 投票:0回答:2

我正在使用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[@]},看起来好像问题出在分隔符上,因为输入似乎是由空格连接在一起的。

我试图寻找一种在保存输入后循环遍历数组并更改定界符的方法。我在StackOverflow上发现了一个有趣的主意:

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

任何帮助将不胜感激。感谢您的时间。

arrays bash scripting newline delimiter
2个回答
0
投票

您不需要此代码段。

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作为输入定界符。

Demo:

$./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
$

0
投票

这里有几个严重的问题,我也有一些更小的建议。第一个(可能)是次要的:您的脚本不是以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_diruserDir或类似名称),而不要使用全大写字母的名称。一堆具有特殊含义的全大写字母名称,如果您使用全大写字母命名,很容易意外地使用其中的一个大写字母而引起混乱。另外,在用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,然后使用“如果不存在该怎么办”部分。说到其中:

    else
        # directory does not exist
        USER_DIR=("/home/$USER")

这是第二个严重的问题。它用指向您的主目录的单个条目替换整个数组。您想要替换当前项的[[only并保留其余的数组,因此请使用USER_DIR[$i]="/home/$USER"。请注意,数组引用周围没有${ } -那些是当您从数组中获取的值时使用的,而不是当您<< setting >>一个的值时使用的。好吧,还有一个建议:与其将所有值读入数组然后再遍历以修复不存在的值,不如在读取目录路径时不进行存在性测试,然后直接添加首先要对数组进行正确处理?您甚至可以在用户输入名称时向用户提供反馈,例如:

#!/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
© www.soinside.com 2019 - 2024. All rights reserved.