2

when I did

date|cut -d' ' -f4|cut -d':' -f1|read time
echo $time

The output was blank

but when I did

date|cut -d' ' -f4|cut -d':' -f1|
while read time
do

    echo $time
done

I get the hour time output...and only 1 output

why is this happening?

thanks

5
  • Is this what you are actually trying to do or is this just an example? If you just need to do this, try time=$(date|cut -d' ' -f4|cut -d':' -f1) Commented Nov 3, 2014 at 1:39
  • This question is covered by BashFAQ #24 ("[W]hy can't I pipe data to read?"): mywiki.wooledge.org/BashFAQ/024 Commented Nov 3, 2014 at 4:19
  • ...you could run read time < <(date | cut -d' ' -f4 | cut -d: -f1) if you wanted to. Commented Nov 3, 2014 at 4:20
  • ...though it would be much more efficient to just tell date to give you only what you wanted, instead of using other programs to extract content: time=$(date '+%H') for just the hour Commented Nov 3, 2014 at 4:21
  • Even better, with very new releases of bash: printf -v time '%(%H)T' -- no subshell, thus even less overhead. Commented Nov 3, 2014 at 4:22

1 Answer 1

1

What you seem to be looking for is something like this:

while true
do
    time=$(date '+%H')
    echo $time
    sleep 0.1
done

Notes

  • As you found, one invocation of date will produce only one date. Connecting it to a while loop will not convince it to produce any more.

  • This is not a reliable way to extract something from date:

    date|cut -d' ' -f4|cut -d':' -f1|read time
    

    It gives me different results depending on whether the day of the month is one or two digits. I think that you are looking to get the hour and that is simply done with:

    date '+%H'
    

    If you want to capture that into a shell variable, use:

    time=$(date '+%H')
    
  • The while loop in the question, if it has no delay, will chew up a lot of CPU time. I added a sleep 0.1 to slow it down.

Why it only works with the while loop

This has to do with environment variables and subshells. Compare and contrast the following two version. In the first version below the output is blank:

$ date | cut -d' ' -f4 | cut -d':' -f1 | read time
$ echo $time

$

In this version the output is non-blank (and the day of the month is printed):

$ date | cut -d' ' -f4 | cut -d':' -f1 | { read time; echo $time; }
2

The issue here is that read time is run in a subshell. This means that any settings to environment variables are lost the moment that the subshell ends. In the latter version there is output because the echo $time statement is run inside the same subshell as read time.

Alternative using redirection adn process substitution

CharlesDuffy points to an alternative approach:

$ read time < <(date '+%H')
$ echo $time
21

This approach uses two steps. First, we use process substitution to create a file-like object: <(date '+%H'). The construct <(...) denotes process substution. It takes a the output of a command, in this case date '+%H' and makes it readable as if it were a file.

The second step is to use read time with its stdin redirected from our newly created file-like object. The result is that the output of date is read into time in the current shell so that the time variable survives.

Note that the space between the angle brackets in < <(date '+%H') is significant. If it is removed, the shell will report a syntax error.

Pipelines and subshells: an advanced feature

In bash, there is a way around this if one sets lastpipe on. This makes the last command in a pipeline run in the current shell. Consequently, any environment variables it assigns will survive. In interactive mode, to turn lastpipe on, one has to turn off job control. Observe:

$ set +m; shopt -s lastpipe
$ date | cut -d' ' -f4 | cut -d':' -f1 | read time
$ echo $time
2
Sign up to request clarification or add additional context in comments.

4 Comments

thank you.... but I'm not really trying to extract anything and I only want date to run once.... just wondering why is read time failing but adding the while makes it work
@demalegabi OK. That behaves that way because, when an environment variable is set inside a subshell, its value is lost when the subshell ends. I updated the answer with some information on that.
It's worth noting or suggesting the use of read < <(date ...) to push the read into the main shell while running the date pipeline in a subshell.
@CharlesDuffy Good idea. I added that to the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.