【问题标题】:How to properly read in a text file as a command line argument in shell script如何正确读取文本文件作为 shell 脚本中的命令行参数
【发布时间】:2018-12-02 06:56:46
【问题描述】:

我的问题的标题与其他帖子非常相似,但我没有在我的具体示例中找到任何内容。我必须将文本文件读取为“$1”,然后将值逐行放入数组中。示例:

myscript.sh /path/to/file

我的问题是这种方法行得通吗?

1   #!/bin/bash
2   file="$1"
3   readarray array < file

此代码是否会将“路径/到/文件”视为“$1”,然后将该路径放入变量“文件”中。如果那部分工作正常,我相信第 3 行应该正确地将这些行放入数组中吗?

这是文本文件的内容:

$ head short - rockyou .txt
290729 123456
79076 12345
76789 123456789
59462 password
49952 iloveyou
33291 princess
21725 1234567
20901 rockyou
20553 12345678
16648 abc123
.
.
.

我希望这是足够的信息来帮助

【问题讨论】:

  • 正确答案取决于文件内容变成数组后会发生什么。另外你需要在第三行写"$file"
  • 在文件变成一个数组后,我必须对其进行排序,然后取出我相信我知道该怎么做的特定行。我刚刚被卡住了,因为我应该使用“$1”来阅读,我不能使用 read -r
  • 那么,这很可能是一个错误的方法!您也可以轻松地对文件进行排序和提取行。
  • 您是否已经根据@karakfa 的反馈更正了您的代码?你还有别的问题吗?
  • 不要使用 'file' 作为变量名(例如使用 f1)。一个简单的测试(将“file”替换为“f1”)和readarray array &lt; $f1 并添加一行echo "${array[@]}"似乎列出了每个条目一行的内容。

标签: bash shell


【解决方案1】:

非常接近。 :)

#!/usr/bin/env bash
case $BASH_VERSION in ''|[123].*) echo "ERROR: Bash 4.0 needed" >&2; exit 1;; esac

file="$1"
readarray -t array <"$file"

declare -p array >&2 # print the array to stderr for demonstration/proof-of-concept

注意readarray 使用-t 参数(丢弃尾随换行符),以及使用$file 而不仅仅是file

【讨论】:

    【解决方案2】:

    我使用以下方法将文件的行放入数组中:

    IFS=$'\r\n' GLOBIGNORE='*' command eval  'array=($(<filename))'
    

    这会获取所有列,您以后可以使用它。

    编辑:对上述过程的解释:

    • IFS=$'\r\n':代表“内部字段分隔符”。 shell 使用它来确定如何进行分词,即。 e.如何识别单词边界。
    • GLOBIGNORE='*':来自 bash 的手册页:以冒号分隔的模式列表,定义了路径名扩展要忽略的文件名集。如果与路径名扩展模式匹配的文件名也匹配 GLOBIGNORE 中的模式之一,则将从匹配列表中删除。
    • command eval:command eval 的加入可以让表达式保持在当前的执行环境中
    • array=...:简单的定义。

    Stackoverflow 和 Stackexchange 上有不同的线程,并提供了更多详细信息: https://unix.stackexchange.com/questions/184863/what-is-the-meaning-of-ifs-n-in-bash-scripting https://unix.stackexchange.com/questions/105465/how-does-globignore-work Read lines from a file into a Bash array

    然后我只是像这样循环数组:

    for (( b = 0; b < ${#array[@]}; b++ )); do
    #Do Somethng
    done
    

    这可能是见仁见智。请等待更多的 cmets。

    编辑:空行和全局的用例

    在昨天的比赛之后。我终于有时间测试建议(空行,带球的行)

    在这两种情况下,与 awk 一起使用时,数组都可以正常工作。在以下示例中,我尝试仅将 column2 打印到新的文本文件中:

    IFS=$'\r\n' GLOBIGNORE='*' command eval  'array=($(<'$1'))'
    for (( b = 0; b < ${#array[@]}; b++ )); do    
    echo "${array[b]}" | awk -F "/| " '{print $2}' >> column2.txt
    done
    

    从以下文本文件开始:

    290729 123456
    79076 12345
    76789 123456789
    59462 password
    49952 iloveyou
    33291 princess
    21725 1234567
    20901 rockyou
    20553 12345678
    16648 abc123
    
    
    
    
    
    20901 rockyou
    20553 12345678
    16648 abc123
    /*/*/*/*/*/*
    20901 rockyou
    20553 12345678
    16648 abc123
    

    清除脚本中的空行和 glob。 执行结果如下:

    123456
    12345
    123456789
    password
    iloveyou
    princess
    1234567
    rockyou
    12345678
    abc123
    
    
    
    
    
    rockyou
    12345678
    abc123
    *
    rockyou
    12345678
    abc123
    

    阵列按预期工作的明确证据。

    执行示例:

    adama@galactica:~$ ./processing.sh test.txt
    adama@galactica:~$ cat column2.txt
    123456
    12345
    123456789
    password
    iloveyou
    princess
    1234567
    rockyou
    12345678
    abc123
    
    
    
    
    
    rockyou
    12345678
    abc123
    *
    rockyou
    12345678
    abc123
    

    如果我们希望删除空行(因为它在输出中对我没有意义),我们可以在 awk 中通过更改以下行来实现:

    echo "${array[b]}" | awk -F "/| " '{print $2}' >> column2.txt
    

    添加/./

    echo "${array[b]}" | awk -F "/| " '/./ {print $2}' >> column2.txt
    

    最终结果:

    123456
    12345
    123456789
    password
    iloveyou
    princess
    1234567
    rockyou
    12345678
    abc123
    rockyou
    12345678
    abc123
    *
    rockyou
    12345678
    abc123
    

    如果您希望将其应用于整个文件(而不是逐列),您可以查看以下线程: AWK remove blank lines

    编辑: rm 上的安全问题:

    我实际上继续在测试文件中放置了 $(rm -rf ~) 以测试在虚拟机上会发生什么:

    Test.txt 内容现在:

    290729 123456
    79076 12345
    76789 123456789
    59462 password
    49952 iloveyou
    33291 princess
    21725 1234567
    20901 rockyou
    20553 12345678
    16648 abc123
    $(rm -rf ~)
    
    
    
    
    
    20901 rockyou
    20553 12345678
    16648 abc123
    /*/*/*/*/*/*
    20901 rockyou
    20553 12345678
    16648 abc123
    

    执行:

    adama@galactica:~$ ./processing.sh test.txt
    adama@galactica:~$ ll
    total 28
    drwxr-xr-x 3 adama adama 4096 dic  1 22:41 ./
    drwxr-xr-x 3 root  root  4096 dic  1 19:27 ../
    drwx------ 2 adama adama 4096 dic  1 22:38 .cache/
    -rw-rw-r-- 1 adama adama  144 dic  1 22:41 column2.txt
    -rwxr-xr-x 1 adama adama  182 dic  1 22:41 processing.sh*
    -rw-r--r-- 1 adama adama  286 dic  1 22:39 test.txt
    -rw------- 1 adama adama 1545 dic  1 22:39 .viminfo
    adama@galactica:~$ cat column2.txt
    123456
    12345
    123456789
    password
    iloveyou
    princess
    1234567
    rockyou
    12345678
    abc123
    -rf
    
    
    
    
    rockyou
    12345678
    abc123
    *
    rockyou
    12345678
    abc123
    

    对系统没有影响。 注意:我在 VM 上使用 Ubuntu 18.04 x64 LTS。最好不要尝试使用 root 测试安全问题。

    编辑set -f必要性:

    adama@galactica:~$ ./processing.sh a
    adama@galactica:~$ cat column2.txt
    [a]
    adama@galactica:~$
    

    没有set -f也能完美运行

    BR

    【讨论】:

    • OP 的解决方案基本上做同样的事情,但更好地处理空行和 glob
    • 没有遇到过这个问题。将用空行填充一个文件,看看会发生什么。我还没想到空行。
    • 也可以试试/*/*/*/*/*/*一行
    • 顺便说一句,您是否考虑过readarray -t array &lt;filename(为了简单性而受到可移植性影响)或IFS=$'\r\n' read -r -d '' -a array &lt; &lt;(cat -- filename &amp;&amp; printf '\0')(使用eval 对当前代码进行安全性胜利,并允许cat被其他进程替换,并确保检测到失败的退出状态并通过,因为read 将返回 false,除非在输入流的末尾看到尾随 NUL。
    • 您也可以通过简单地在 命令替换 中使用重定向来消除 eval,这将是一种改进,例如IFS=$'\r\n'; array=( $(&lt;rockyou.txt) )。不如readarraymapfile,但比eval好。
    猜你喜欢
    • 1970-01-01
    • 2021-07-20
    • 1970-01-01
    • 2013-10-22
    • 2014-02-24
    • 2013-03-03
    • 1970-01-01
    • 2015-06-14
    • 2016-12-08
    相关资源
    最近更新 更多