7

I am simply trying to get a bash script
to return the content of a text file.

#!/bin/bash
cat $1

the problem is.. when the path to a file is not fed to this bash script... how will it even close itself.

because via regular command line... if there is no file path being fed to the cat command.. it does not hang up.

almost as if it is waiting for something..

4
  • 6
    cat is waiting for input from stdin which is how it's designed. If this is a concern for you, then you should be validating your inputs (which you should be doing anyways) by doing a -z test or something.
    – Bratchley
    Commented Jun 20, 2014 at 17:29
  • Just noticed @Creek posted this answer below. That's what you need to do. The cat command is doing what it needs to do.
    – Bratchley
    Commented Jun 20, 2014 at 17:30
  • 6
    Tip: always do set -u at the start of each script, at least you'll have an indication that there's a problem in the script arguments instead of calling potentially destructive commands with the wrong parameters. Commented Jun 20, 2014 at 23:18
  • 1
    Default $1 to /dev/null if it does not exist.
    – smci
    Commented Jun 21, 2014 at 4:40

7 Answers 7

35
#!/bin/bash
if [[ "$#" -ne 1 ]]; then
    echo "Usage: $0 [INPUT FILE]" 1>&2
    exit 1
fi
cat "$1"
8
  • Above and beyond is generally best.
    – mikeserv
    Commented Jun 20, 2014 at 17:30
  • @mikeserv who? where?
    – Creek
    Commented Jun 20, 2014 at 17:47
  • 4
    this is the better solution in my opinion, since it exits with error
    – polym
    Commented Jun 20, 2014 at 17:49
  • 1
    @Creek - you, here.
    – mikeserv
    Commented Jun 20, 2014 at 18:18
  • 1
    There are lots of votes for this answer but perhaps because this is an english only web site .. people from other parts of the world are not really voting.. it is not balanced. simplicity saves the day with the chosen answer.
    – user72685
    Commented Jun 20, 2014 at 18:19
18

Your command also has another problem, what if the filename has spaces?

#!/bin/bash
cat "$1"

Always quote unless you have a compelling reason not to. And cat "" does not hang. Instead it would produce an error message like "No such file or directory".

5
  • 4
    cat "" doesn't hang, but it does print an error message: cat: : No such file or directory (referring to the empty string). It's better to test whether $1 exists, probably by checking the value of $#. Commented Jun 20, 2014 at 20:10
  • The main problem I see here is that if one is running a file system which allows an empty file name this could actually work and not error. However, as a quick hack this is fine.
    – Vality
    Commented Jun 20, 2014 at 21:06
  • @Darkhogg: If there is such a file the test would succeed also. I assume the OP's script is in reality more complicated and this was just an example regarding cat invocation. Other answers went more to the error handling route. Someone even deleted his answer; oh well. something for everyone here. Commented Jun 20, 2014 at 21:58
  • Not cat "$@"?
    – Cascabel
    Commented Jun 20, 2014 at 22:55
  • @vality, empty filename are likely to be illegal on all (POSIX) operating systems: how would you differentiate a filename /path/to/$FILENAME and the parent directory /path/to/ ? Commented Sep 19, 2016 at 11:10
7

cat will ignore STDIN if you supply it with a filename. Connecting STDIN to /dev/null would be a valid solution in this context.

$ cat test.sh
#!/bin/bash
cat $1 < /dev/null
$ ./test.sh test.sh
#!/bin/bash
cat $1 < /dev/null
$ ./test.sh
$

The shortcoming of this approach is that it will return 0 if no file name is supplied. No way around that. Failure to open the specified file will still return failure.

2

I would say that cat is an unsuitable tool here and this is why you are getting this undesired behaviour, cat is designed to concatenate a set of files and write the output to another file, it is a quirk of the implementation that the default input is stdin and the default output is stdout.

I would say a far more suitable command here would be:

#!/bin/bash
cp ${1} /dev/stdout

The dev stdout directory is originally Linux only, however before I am yelled at for using a platform specific construct bash (the shell the op explicitly stated they are using) implements /dev/stdout as a builtin so is therefore usable on any platform with bash or ksh.

cp is a utility more suitable here as it is designed to copy the contents from one file to another and that is exactly what we are doing here, copying $1 to stdout

This command will also usefully fail given no input and works on any system with bash, note it does assume that the user has properly escaped any file names with odd chars like space or \n.

6
  • 3
    Honestly, this seems to me like an overcomplication; cat was born to concatenate files, but everybody uses it just to show files contents probably since the first version of UNIX, and I see nothing wrong with that - cat "filename" is way shorter, more portable and more idiomatic than cp filename /dev/stdout. Commented Jun 20, 2014 at 23:16
  • @MatteoItalia I give you that it is shorter, and potentially more idiomatic but it is not significantly more portable, every shell I have seen in over a decade (with the possible exception of csh) had implemented the cp version, I will note that there is a performance impact if your chosen shell makes one a builtin and the other an external command. However if neither are builtin cp will be faster. Regarding the expressiveness I suppose it depends what you are used to.
    – Vality
    Commented Jun 21, 2014 at 10:20
  • @Vality Why should cp be faster if both aren't builtin? Commented Jun 22, 2014 at 15:58
  • @heinrich5991 I have benchmarked this for a 512MB text file in a variety of machines, on average with these machines cat takes 1.79 seconds and cp takes 1.42 seconds. It seems from running a performance analyzer that cat is copying in small chunks and running a loop for every char printed, cp just splices the files together at high speed.
    – Vality
    Commented Jun 22, 2014 at 18:08
  • I came across this and just thought I'd mention that even in bash, on Linux, a script might not have write permission for files/descriptors like /dev/stdout. I've encountered this in Nix build scripts, for example having to use 1>&2 rather than 1> /dev/stderr since there's no write permission for /dev/stderr.
    – Warbo
    Commented Aug 14, 2017 at 22:18
2

To display file contents only if the file exists:

#!/bin/bash
[ -f "$1" ] && cat "$1"

This script assumes that the first argument is the file you want to cat

1
0

Default $1 to /dev/null if it does not exist.

(You can easily figure out the bash syntax for that)

0
: | cat $1

This would be the shortest piece of code that would work. It should ignore the absence of a file silently.

Howeverm, is this really what you need? I agree with @Vality that you probably should not use cat in this context.

0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .