2

Each line in a given file 'a.txt' contains the directory/path to another unique file. Suppose we want to parse 'a.txt' line-by-line, extract the path in string format, and then use a tool such as vim to process the file at this path, and so on.

After going through this thread - Read a file line by line assigning the value to a variable, I wrote the following script, say 'open-file.sh' on bash (I'm new to it)

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
  vim -c ":q" -cq $line # Just open the file and close it using :q 
done < "$1"

We would then run the above script as -

./open-file.sh a.txt

The problem is that although the path to a new file is correctly specified by $line, when vim opens the file, vim continues to receive the text contained in 'a.txt' as a command. How can I write a script where I can correctly obtain the path from 'a.txt', open it using vim, and then continue parsing the remaining lines in 'a.txt' ?

V-Red
  • 239
  • 2
  • 17

3 Answers3

2

Replace:

vim -c ":q" -cq $line

With:

vim -c ":q" -cq "$line" </dev/tty

The redirection </dev/tty tells vim to take its standard input from the terminal. Without that, the standard input for vim is "$1".

Also, it is good practice to put $line in double-quotes to protect it from word splitting, etc.

Lastly, while vim is excellent for interactive work, if your end-goal is fully automated processing of each file, you might want to consider tools such as sed or awk.

John1024
  • 109,961
  • 14
  • 137
  • 171
0

Although I'm not sure of your ultimate goal, this shell command will execute vim once per line in a.txt:

xargs -o -n1 vim -c ':q' < a.txt

As explained in the comments to Read a file line by line assigning the value to a variable, the issue you're encountering is due to the fact that vim is an interactive program and thus continues to read input from $line.

telenachos
  • 884
  • 6
  • 18
  • `xargs -o` is not available in my `GNU findutils 4.7.0`. From `man xargs` it seems `-o` is specific to BSD. **Quote:** *`xargs sh -c 'emacs "$@" < /dev/tty' emacs` [...] This example achieves the same effect as BSD's -o option, but in a more flexible and portable way.* – Socowi Jan 17 '19 at 22:47
  • Interesting and good to be aware of. I am also using `GNU findutils 4.7.0` on Ubuntu and the man page states that `-o` was added for better compatibility with BSD. – telenachos Jan 17 '19 at 22:52
  • That's strange. The full version is *`xargs (GNU findutils) 4.7.0-git` `Copyright (C) 2016 [...]`*. I'm on Ubuntu 16.04.3 LTS in the Windows subsystem for Linux (WSL). When I try to execute the command I get `xargs: invalid option -- 'o'`. – Socowi Jan 17 '19 at 22:56
  • I'm on Ubuntu 18.04 so I guess they added it fairly recently. – telenachos Jan 17 '19 at 22:59
0

The problem was already mentioned in a comment under the answer you based your script on.

vim is consuming stdin which is given to the loop by done < $1. We can observe the same behavior in the following example:

$ while read i; do cat; done < <(seq 3)
2
3

<(seq 3) simulates a file with the three lines 1, 2, and 3. Instead of three silent iterations we get only one iteration and the output 2 and 3.

stdin is not only passed to read in the head of the loop, but also to cat in the body of the loop. Therefore read reads one line, the loop is entered, cat reads all remaining lines, stdin is empty, read has nothing to read anymore, the loop exits.

You could circumvent the problem by redirecting something to vim, however there is an even better way. You don't need the loop at all:

< "$1" xargs -d\\n -n1 vim -c :q -cq

xargs will execute vim once for every line in the file given by $1.

Socowi
  • 25,550
  • 3
  • 32
  • 54