1

Hi i run a command on Linux server.

tail -3 `ls -1t nnnn* | head -1`

When nnnn file exist, all good.

When nnnn file not exist the tty is hanged until ^C

nt-home-stg>> tail -3 `ls -1t nnnn* | head -1`
ls: No match.

After the ls: No match. it hangs until ctrl c (^c) is pressed.

I've searched the web and could not find an answer.

Already tried the traditional suppress >& /dev/null and 2>/dev/null . Did not help.

Is there a way for the command to end and not hang?

Rubén
  • 34,714
  • 9
  • 70
  • 166
ReDevil
  • 77
  • 6
  • Incidentally, \` is not an "acute"; an acute accent would point the other way, and wouldn't exist on its own anyway. It's commonly called a "backtick". – IMSoP Mar 16 '17 at 14:41
  • @IMSoP Not trying to argue, but I took the name from here http://www.computerhope.com/keys.htm Just to act like pro :) I BTW Know it by the name 'back quote'. – ReDevil Mar 16 '17 at 15:26
  • Yeah, that website is kind of ... wrong. They list it as both [grave accent](https://en.wikipedia.org/wiki/Grave_accent) and [acute accent](https://en.wikipedia.org/wiki/Acute_accent), which are definitely not the same thing. It also says it doesn't exist on phone keyboards, when it's perfectly easy to find on the default Google Android keyboard. – IMSoP Mar 16 '17 at 15:46

1 Answers1

2

The backtick syntax substitutes the output of the given command; when that command returns a filename, you get something like:

tail -3 nnnn01.whatever

But if there are no matches, there is nothing to substitute, so you effectively run:

tail -3

If we look at the summary under man tail, we see the behaviour if tail is not given a filename:

With no FILE, or when FILE is -, read standard input.

So tail is waiting for some input to be piped in, and it will then display the last 3 lines of that input. You can actually type a few lines of text, and press Ctrl-D ("end of file"), and you'll see it happen.

This may seem pointless, but the command doesn't know (or care) that it's been invoked directly, and not as part of a pipeline. So the head -1 in your inner query is actually doing the same thing: reading standard input because you didn't give a file name.

To avoid this, you should test that your file exists first, before running tail. A non-elegant way of writing that on one line would be to capture the filename in a variable, and use [ (aka test) to assert that it is non-empty:

(file=`ls -1t nnnn* | head -1`; [ -n "$file" ] && tail -3 $file)

This will still give you the warning from ls that the glob failed, but will skip the hanging tail.

IMSoP
  • 89,526
  • 13
  • 117
  • 169
  • Thanks - is there a way to not wait for standard input. and just end the command in such case? – ReDevil Mar 16 '17 at 11:34
  • 1
    @ReDevil I'd say the best approach would be to test your file exists first. A non-elegant way of writing that on one line: `(file=$(ls -1t nnnn* | head -1); [ -n "$file" ] && tail -3 $file)` (Using `$(...)` instead of backticks, because Markdown makes me sad, but also because I prefer that style anyway.) – IMSoP Mar 16 '17 at 11:44