-3

Sorry if it's long and confusing, but I have three files, namely cat, dog, and cow.

My code is something like this:

echo -e "Enter file name: \c" read filename #saves variable if [ -e "$filename" ] then total =$(wc -l $filename) #calculate total lines in the*CAT* file echo There are $total #prints There are *numberoflines* echo $total > newfile #writes the answer into a newly created file else echo $filename not found fi 

When I run the above code, the following happens:

 Enter file name : cat There are 5 cat 

(I input cat at the prompt and I see the number of lines in the file.)

It will give me an output of 5 cat when I run the file called cat which has 5 lines in it.

But let's say when I want to run all three files at the same time, what do I have to do?

Let's say I want it to display as following

Enter file name : cat dog cow 5 cat 2 dog 14 cow Total 21 

Unfortunately, it displays

file not found 

when I run all files at the same time, but if I run the script on the files one by one, nothing is wrong.

11
  • Please fix your shell script. (1) Quote your variables, (2) Remove space at total =.... (3) You're missing a fi. Commented May 18, 2021 at 8:45
  • Of course it does not work. Your command is [ -e "cat dog cow" ], but a file with name "cat dog cow" does not exist. Commented May 18, 2021 at 8:47
  • I tried to run your script with tcsh as you tagged but I get -e Enter file name: read: Command not found. filename: Undefined variable. Commented May 18, 2021 at 8:50
  • Your script does not display file not found when run with the tcsh shell. It says -e Enter file name: \c followed by the error read: Command not found.. If you run it with bash, it terminates with script: line 10: syntax error: unexpected end of file. What shell are you actually using? Commented May 18, 2021 at 8:59
  • 1
    @WenhanXiao YOur script still contains errors as already written in comments. Consider using shellcheck.net to check your script. In case you re-typed your script, please copy&paste exactly the script you run on your system and copy&paste the exact input/output/error messages. Instead of using the read command to get the file name(s) I suggest to use command line arguments. You could run your script as linecounter.sh cat dog cow and implement it like for filename in "$@"; do ... ; done Commented May 18, 2021 at 9:26

1 Answer 1

1

Apart from the syntax issues already mentioned in comments, your main issue with the code is that you are reading what you perceive to be multiple filenames into a single string, then you use that single string as a single filename.

Instead of correcting that aspect of the script, let's rethink what it is that you want to do.

You want the user to supply one or several filenames, then you want your script to count the number of lines in each and output the number of lines in each file, along with the total number of lines. You also want to allow the user to store this information to an output file. If a filename does not exist, you want the user to by made aware of this.

The script could look like this:

#!/bin/sh - wc -l -- "$@" 

Testing this without storing the output:

$ ./script.sh cat dog cow 5 cat 2 dog 14 cow 21 total 

Calling the script with a filename that does not exist:

$ ./script.sh cat dog cow hamster 5 cat 2 dog 14 cow wc: hamster: No such file or directory 21 total 

Storing the output in a file:

$ ./script.sh cat dog cow hamster >output wc: hamster: No such file or directory 
$ cat output 5 cat 2 dog 14 cow 21 total 

The "$@" in the script will be replaced by the list of command line arguments, each individually quoted. This means that if you call your script with cat, dog and cow on the command line, the "$@" in the script will expand to that list.

The wc utility already performs all the necessary things, including providing you with the grand total across all the given files, so all we need to do is to call it with its -l option on the filenames listed on the script's command line to get it to count lines.

The output is written to standard output by default. This gives the user of script the freedom to easily redirect the output to wherever they want.

Also notice that it's far more convinient to give a script command line arguments, using tab completion or filename globbing patterns, than to write each filename in by hand at an interactive prompt. In this case, it also greatly simplifies the script's code.

What you may want to do as an extra check, is to complain to the user if the did not supply any filenames. The wc utility will otherwise start reading from standard input, which would be somewhat confusing.

You could do this in many ways, but I'll show two variants of the enhanced script here:

#!/bin/sh - if [ "$#" -eq 0 ]; then echo 'expected to get filename argument(s)' >&2 exit 1 fi wc -l -- "$@" 

This looks at the $# value, which is the number of command line arguments that the script got. If this is zero, the if statement will branch to the echo, which will output a diagnostic message and then terminate the script.

#!/bin/sh - wc -l -- "${@?expected to get filename argument(s)}" 

This does a similar thing but using a very different mechanics. The ${variable?word} expansion will result in the diagnostic message in word being written to the output if "$@" is empty or unset, and it will also terminate the script.

This may look like:

$ ./script.sh ./script.sh[3]: @: expected to get filename argument(s) 
2
  • 1
    Note that ${@?error} does not work with all shells as several shells always consider $@ to always be set (see also interaction with the nounset option) Commented May 20, 2021 at 14:27
  • 2
    FYI, I've raised austingroupbugs.net/view.php?id=1478 about that. Commented May 23, 2021 at 16:17

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.