1

I'd like to ask how to use cut or other similar command to get date and time... So basically, $line was retrieved from a for loop:

for line in $(cat $file);
do
   getdatetime=$(echo $line | cut -f4 -d,)
done

where the sample value for $line is:

883427446627317909,1114259,1573178423,2019-11-08 02:00:23,RD,4.7,0,351442429

The file $file contain multiple lines similar to the sample $line.

The expected value for getdatetime is:

2019-11-08 02:00:23

But i'm getting the date only:

2019-11-08

Is there any one-liner cut command to extract the date and time? I need to preserve the structure of the for loop. Thank you.

  • 3
    quote your command-substitution getdatetime="$(echo" $line" | cut -f4 -d,)"; Possible duplicate of Why does my shell script choke on whitespace or other special characters? – αғsнιη Nov 08 '19 at 11:44
  • hi @αғsнιη, i have done what you suggested but still, only the date is shown. – dcdum2018 Nov 08 '19 at 13:33
  • 1
    line='883427446627317909,1114259,1573178423,2019-11-08 02:00:23,RD,4.7,0,351442429' ; getdatetime="$(echo "$line" | cut -f4 -d,)"; echo "$getdatetime"? – αғsнιη Nov 08 '19 at 13:45
  • 1
    Could you show us how you assign the value to your variable line? – AdminBee Nov 08 '19 at 13:50
  • Is there more than one line in the input file? Because this loop looks like it will only save the last one -- is that what you want? – JigglyNaga Nov 08 '19 at 17:18
  • hi @JigglyNaga, yup there are multiple lines inside $file and all of the lines have similar construction – dcdum2018 Nov 09 '19 at 12:59
  • Why do you need to use a for loop. A for loop always iterates over a static set of words, which means you would have to let the shell read the complete file into memory only to iterate over it. This is extremely inefficient and inelegant. If you need to use a shell loop at all, you should be using a while loop, which reads individual lines (separately in each iteration). – Kusalananda Nov 09 '19 at 13:04
  • So there are multiple lines of input, but you only want to save the date from the last one? It's possible that you don't need a loop at all. – JigglyNaga Nov 09 '19 at 14:06
  • @Kusalananda @JigglyNaga i am using for loop because i need the first field and the fourth field per each $line. i can easily get the first field but for the fourth field, only the date is fetched – dcdum2018 Nov 09 '19 at 14:18

2 Answers2

2

When you iterate over the unquoted command substitution $(cat $file), you iterate over all words that cat $file results in. A word will be anything delimited by a whitespace (space, tab or newline by default). This means that for a line in $file that is

883427446627317909,1114259,1573178423,2019-11-08 02:00:23,RD,4.7,0,351442429

you will have the two words 883427446627317909,1114259,1573178423,2019-11-08 and 02:00:23,RD,4.7,0,351442429 (i.e. the loop would iterate twice for this single line). This means that you'd get 2019-11-08 in the first iteration and 0 in the second from this line.

The solution is not to quote the command substitution as doing so would cause the loop to iterate once over the full contents of the file which is read into $line. The solution is not to set IFS to a newline, as that is inelegant (it would necessitate one call to cut in each iteration).

Instead, parse out the data that you want in a single call to cut and read that:

while IFS= read -r datetime; do
    # use "$datetime" here
done < <( cut -d, -f4 "$file" )

This uses a process substitution to create an input stream for the while loop to read from. The data in this stream will consist of the 4th comma-delimited field in the file named by $file.

Alternatively, with the while loop in a subshell:

cut -d, -f4 "$file" |
while IFS= read -r datetime; do
    # use "$datetime" here
done

With awk, the processing would be cleaner (unless you need to use the date/time value as a shell variable for whatever reason):

awk -F , '{ datetime = $4; ... more code here using the datetime variable }' "$file"

Related reading:

Kusalananda
  • 333,661
  • hi @Kusalananda, is it possible to get the first field and fourth field at the same time for each $line and store them on different variables? like: getid=883427446627317909 and getdatetime=2019-11-08 02:00:23 – dcdum2018 Nov 09 '19 at 14:22
  • 1
    @dcdum2018 This was not part of the question. The first part of bxm's answer would be easily modifiable to do this, just change the loop to while IFS=, read getid _ _ getdatetime _. – Kusalananda Nov 09 '19 at 14:30
2

We can actually do without cut if we want to:

while IFS=, read _ _ _ stamp _ ; do
  echo "do something with $stamp here"
done < "$file"

Breaking it down a bit:

IFS=, temporarily set the record separator to be ,

read _ _ _ stamp _ store fields 1-3 and 5 onwards in a throwaway variable, while capturing field 4 (your date/time) as stamp

< "$file" read in the source file (this is caught by our read command).

Another way to skin this cat, by populating an array with each line (this works in bash for sure, other shells may not support this or implement it in a different manner):

while IFS=, read -a line ; do echo "${line[3]}" ; done < "$file"
bxm
  • 4,855
  • hi @bxm, this line of code while IFS=, read -a line ; do echo "${line[3]}" ; done < "$file" is not working in #!/bin/ksh. i think the -a part is not working – dcdum2018 Nov 09 '19 at 14:49
  • Must be a bash-ism, you should be fine with the other version (or maybe there’s an option you can pass on ksh which does something similar?) – bxm Nov 09 '19 at 14:51
  • 1
    @dcdum2018 The ksh93 shell has a read that implements reading into an array with read -A. See the manual. – Kusalananda Nov 09 '19 at 16:41