I'm writing my first Bash script, I have some experience with C and C# so I think the logic of the program is correct, it's just the syntax is so complicated because apparently there are many different ways to write the same thing!
我正在编写我的第一个Bash脚本,我对C和C#有一些经验,所以我认为程序的逻辑是正确的,只是语法是如此复杂,因为显然有很多不同的方法来编写同样的东西!
Here is the script, it simply checks if the argument (string) is contained in a certain file. If so it stores each line of the file in an array and writes an item of the array in a file. I'm sure there must be easier ways to achieve that but I want to do some practice with bash loops
这是脚本,它只是检查参数(字符串)是否包含在某个文件中。如果是这样,它将文件的每一行存储在一个数组中,并将该数组的项写入文件中。我确信必须有更简单的方法来实现这一点,但我想用bash循环做一些练习
#!/bin/bash
NOME=$1
c=0
#IF NAME IS FOUND IN THE PHONEBOOK THEN STORE EACH LINE OF THE FILE INTO ARRAY
#ONCE THE ARRAY IS DONE GET THE INDEX OF MATCHING NAME AND RETURN ARRAY[INDEX+1]
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY"
while read line
do
myArray[$c]=$line # store line
c=$(expr $c + 1) # increase counter by 1
done < /root/phonebook.txt
else
echo "Name not found"
fi
c=0
for i in myArray;
do
if myArray[$i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
fi
done
This code returns the only the second item of myArray
(myArray[2]
) or the second line of the file, why?
这段代码只返回myArray的第二项(myArray [2])或文件的第二行,为什么?
2 个解决方案
#1
2
The first part (where you build the array) looks ok, but the second part has a couple of serious errors:
第一部分(构建数组的位置)看起来不错,但第二部分有几个严重的错误:
-
for i in myArray;
-- this executes the loop once, with $i set to "myArray". In this case, you want $i to iterate over the indexes of myArray, so you need to use我在myArray中; - 这会执行循环一次,$ i设置为“myArray”。在这种情况下,您希望$ i迭代myArray的索引,因此您需要使用
for i in "${!myArray[@]}"
or
要么
for ((i=0; i<${#a[@]}; i++))
(although I generally prefer the first, since it'll work with noncontiguous and associative arrays).
(虽然我通常更喜欢第一个,因为它适用于非连续和关联数组)。
Also, you don't need the
;
unlessdo
is on the same line (in shell,;
is mostly equivalent to a line break so having a semicolon at the end of a line is redundant).另外,你不需要;除非do在同一行上(在shell中,;大部分相当于换行符,因此在行尾有一个分号是多余的)。
-
if myArray[$i]="$NOME" ; then
-- theif
statement takes a command, and will therefore treatmyArray[$i]="$NOME"
as an assignment command, which is not at all what you wanted. In order to compare strings, you could use thetest
command or its synonym[
如果myArray [$ i] =“$ NOME”;然后 - if语句接受一个命令,因此将myArray [$ i] =“$ NOME”视为一个赋值命令,这根本不是你想要的。为了比较字符串,您可以使用test命令或其同义词[
if [ "${myArray[i]}" = "$NOME" ]; then
or a bash conditional expression
或者bash条件表达式
if [[ "${myArray[i]}" = "$NOME" ]]; then
The two are very similar, but the conditional expression has much cleaner syntax (e.g. in a test command,
>
redirects output, while\>
is a string comparison; in[[ ]]
a plain>
is a comparison).两者非常相似,但条件表达式具有更清晰的语法(例如,在测试命令中,>重定向输出,而\>是字符串比较;在[[]]中,普通>是比较)。
In either case, you need to use an appropriate
$
expression for myArray, or it'll be interpreted as a literal. On the other hand, you don't need a$
before thei
in "${myArray[i]}" because it's in a numeric expression context and therefore will be expanded automatically.在任何一种情况下,您都需要为myArray使用适当的$表达式,否则它将被解释为文字。另一方面,在“$ {myArray [i]}”中你不需要$之前的$,因为它在数值表达式上下文中,因此会自动扩展。
Finally, note that the spaces between elements are absolutely required -- in shell, spaces are very important delimiters, not just there for readability like they usually are in c.
最后,请注意元素之间的空格是绝对必需的 - 在shell中,空格是非常重要的分隔符,而不仅仅是它们通常在c中的可读性。
#2
2
1.-This is what you wrote with small adjustments
1.-这是你用小调整写的
#!/bin/bash
NOME=$1
#IF NAME IS FOUND IN THE PHONE-BOOK **THEN** READ THE PHONE BOOK LINES INTO AN ARRAY VARIABLE
#ONCE THE ARRAY IS COMPLETED, GET THE INDEX OF MATCHING LINE AND RETURN ARRAY[INDEX+1]
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
IFS= while read -r line #IFS= in case you want to preserve leading and trailing spaces
do
myArray[c]=$line # put line in the array
c=$((c+1)) # increase counter by 1
done < /root/phonebook.txt
for i in ${!myArray[@]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
fi
done
else
echo "Name not found"
fi
2.-But you can also read the array and stop looping like this:
2.-但您也可以读取数组并停止循环:
#!/bin/bash
NOME=$1
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
readarray myArray < /root/phonebook.txt
for i in ${!myArray[@]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
break # stop looping
fi
done
else
echo "Name not found"
fi
exit 0
3.- The following improves things. Supposing a)$NAME matches the whole line that contains it and b)there's always one line after a $NOME found, this will work; if not (if $NOME can be the last line in the phone-book), then you need to do small adjustments.
3.-以下改进了一些事情。假设a)$ NAME匹配包含它的整行,b)在找到$ NOME之后总是有一行,这将起作用;如果没有(如果$ NOME可以是电话簿中的最后一行),那么你需要做一些小的调整。
!/bin/bash
PHONEBOOK="/root/phonebook.txt"
NUMBERTOCALL="/root/numbertocall.txt"
NOME="$1"
myline=""
myline=$(grep -A1 "$NOME" "$PHONEBOOK" | sed '1d')
if [ -z "$myline" ]; then
echo "Name not found :-("
else
echo -n "$NOME FOUND.... "
echo "$myline" >> "$NUMBERTOCALL"
echo " .... AND SAVED! :-)"
fi
exit 0
#1
2
The first part (where you build the array) looks ok, but the second part has a couple of serious errors:
第一部分(构建数组的位置)看起来不错,但第二部分有几个严重的错误:
-
for i in myArray;
-- this executes the loop once, with $i set to "myArray". In this case, you want $i to iterate over the indexes of myArray, so you need to use我在myArray中; - 这会执行循环一次,$ i设置为“myArray”。在这种情况下,您希望$ i迭代myArray的索引,因此您需要使用
for i in "${!myArray[@]}"
or
要么
for ((i=0; i<${#a[@]}; i++))
(although I generally prefer the first, since it'll work with noncontiguous and associative arrays).
(虽然我通常更喜欢第一个,因为它适用于非连续和关联数组)。
Also, you don't need the
;
unlessdo
is on the same line (in shell,;
is mostly equivalent to a line break so having a semicolon at the end of a line is redundant).另外,你不需要;除非do在同一行上(在shell中,;大部分相当于换行符,因此在行尾有一个分号是多余的)。
-
if myArray[$i]="$NOME" ; then
-- theif
statement takes a command, and will therefore treatmyArray[$i]="$NOME"
as an assignment command, which is not at all what you wanted. In order to compare strings, you could use thetest
command or its synonym[
如果myArray [$ i] =“$ NOME”;然后 - if语句接受一个命令,因此将myArray [$ i] =“$ NOME”视为一个赋值命令,这根本不是你想要的。为了比较字符串,您可以使用test命令或其同义词[
if [ "${myArray[i]}" = "$NOME" ]; then
or a bash conditional expression
或者bash条件表达式
if [[ "${myArray[i]}" = "$NOME" ]]; then
The two are very similar, but the conditional expression has much cleaner syntax (e.g. in a test command,
>
redirects output, while\>
is a string comparison; in[[ ]]
a plain>
is a comparison).两者非常相似,但条件表达式具有更清晰的语法(例如,在测试命令中,>重定向输出,而\>是字符串比较;在[[]]中,普通>是比较)。
In either case, you need to use an appropriate
$
expression for myArray, or it'll be interpreted as a literal. On the other hand, you don't need a$
before thei
in "${myArray[i]}" because it's in a numeric expression context and therefore will be expanded automatically.在任何一种情况下,您都需要为myArray使用适当的$表达式,否则它将被解释为文字。另一方面,在“$ {myArray [i]}”中你不需要$之前的$,因为它在数值表达式上下文中,因此会自动扩展。
Finally, note that the spaces between elements are absolutely required -- in shell, spaces are very important delimiters, not just there for readability like they usually are in c.
最后,请注意元素之间的空格是绝对必需的 - 在shell中,空格是非常重要的分隔符,而不仅仅是它们通常在c中的可读性。
#2
2
1.-This is what you wrote with small adjustments
1.-这是你用小调整写的
#!/bin/bash
NOME=$1
#IF NAME IS FOUND IN THE PHONE-BOOK **THEN** READ THE PHONE BOOK LINES INTO AN ARRAY VARIABLE
#ONCE THE ARRAY IS COMPLETED, GET THE INDEX OF MATCHING LINE AND RETURN ARRAY[INDEX+1]
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
IFS= while read -r line #IFS= in case you want to preserve leading and trailing spaces
do
myArray[c]=$line # put line in the array
c=$((c+1)) # increase counter by 1
done < /root/phonebook.txt
for i in ${!myArray[@]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
fi
done
else
echo "Name not found"
fi
2.-But you can also read the array and stop looping like this:
2.-但您也可以读取数组并停止循环:
#!/bin/bash
NOME=$1
c=0
if grep "$NOME" /root/phonebook.txt ; then
echo "CREATING ARRAY...."
readarray myArray < /root/phonebook.txt
for i in ${!myArray[@]}; do
if myArray[i]="$NOME" ; then
echo ${myArray[i+1]} >> /root/numbertocall.txt
break # stop looping
fi
done
else
echo "Name not found"
fi
exit 0
3.- The following improves things. Supposing a)$NAME matches the whole line that contains it and b)there's always one line after a $NOME found, this will work; if not (if $NOME can be the last line in the phone-book), then you need to do small adjustments.
3.-以下改进了一些事情。假设a)$ NAME匹配包含它的整行,b)在找到$ NOME之后总是有一行,这将起作用;如果没有(如果$ NOME可以是电话簿中的最后一行),那么你需要做一些小的调整。
!/bin/bash
PHONEBOOK="/root/phonebook.txt"
NUMBERTOCALL="/root/numbertocall.txt"
NOME="$1"
myline=""
myline=$(grep -A1 "$NOME" "$PHONEBOOK" | sed '1d')
if [ -z "$myline" ]; then
echo "Name not found :-("
else
echo -n "$NOME FOUND.... "
echo "$myline" >> "$NUMBERTOCALL"
echo " .... AND SAVED! :-)"
fi
exit 0