0

I am probably making an obvious mistake but I could use a strong hint:-)

for file in *; do ls $file | var=$(ls $file | grep -Eo '.{18}$' | cut -d '.' -f1 |sed 's/.\{12\}/&./'); echo "var is $var. File is $file";done

RESULT: $var is empty. $file is not. Why is $var empty?

When I manually put my test string in $file and run as follows, $var contains what I want:

var=$(ls $file | grep -Eo '.{18}$' | cut -d '.' -f1 |sed 's/.\{12\}/&./')

So there is something about the for loop which is causing this. It seems the $var variable has a scope limitation preventing me from using in following statements.

Have a great Easter:-)

full example:

As asked, here is an example to hopefully clarify my query. The file's inventory_a_test-20190605161153.txt1 date must match the date in its file name. First, I set the file to the wrong date:

$ touch inventory_a_test-20190605161153.txt
$ ls -l --full-time inventory_a_test-20190605161153.txt
-rw-rw-r-- 1 root root 0 **2021-04-05** 15:02:05.000000000 +0200 inventory_a_test-20190605161153.txt

Now I want to write a command flow like the one below as a for loop in bash to correct the date:

$ file=inventory_a_test-20190605161153.txt
$ ls $file | grep -Eo '.{18}$' | cut -d '.' -f1 |sed 's/.\{12\}/&./'
201906051611.53
$ touch -a -m -t 201906051611.53 $file
$ ls -l --full-time inventory_a_test-20190605161153.txt
-rw-rw-r-- 1 root root 0 **2019-06-05** 16:11:53.000000000 +0200   inventory_a_test-20190605161153.txt
5
  • 1
    The limitation is that each command in a Bash pipeline runs in its own subshell scope. The value of var is locally defined only between var= and the matching ;. Apr 5, 2021 at 12:14
  • 2
    What do you want to achieve by the pipeline ls $file | var=(...) in the loop? It looks as if the command substitution is not reading the output of that ls command.
    – Kusalananda
    Apr 5, 2021 at 12:18
  • $file contains a file name. In the file name is a time stamp which needs some formatting. After the grep and sed commands, $var contains, for instance, 201906051611.53. I then want to use $var with touch to alter the file timestamp
    – Jeroen1000
    Apr 5, 2021 at 12:23
  • @Jeroen1000 please edit your question and add some example file names and the output you expect from them. This isn't a very safe approach.
    – terdon
    Apr 5, 2021 at 12:53
  • @Jeroen1000 thanks for the edit, that is helpful. Please see my updated answer.
    – terdon
    Apr 5, 2021 at 14:50

2 Answers 2

3

This part doesn't make sense:

ls $file | $(something)

The $(command) format means "run command and treat its output as a variable". This isn't something that can accept piped input. I think what you meant to write was:

for file in *; do 
    var=$(ls "$file" | grep -Eo '.{18}$' | cut -d '.' -f1 |sed 's/.\{12\}/&./'); 
    echo "var is $var. File is $file"
done

However, this will fail quite badly for strange file names. You really don't want to be parsing the output of ls. More importantly, the ls is absolutely not needed here! You already have the file name so the ls is adding nothing but a complication and possible source of error. This should do what you want:

for file in *; do
    timestamp=$(sed -E 's/.*(.{12})(..)\.[^.]*$/\1.\2/' <<<"$file")
    touch -a -m -t "$timestamp" -- "$file"
done

The sed will print only the last 14 characters of the file that come before the final extension (\.[^.]*$/ will match a . and then 0 or more non-. until the end of the file name), but separates the last 2 from the previous 12 with a . effectively generating your timestamp.

2
  • No, thank you for that kind solution. I understand the regex but your sofistication is well beyond what I could have come up with:)
    – Jeroen1000
    Apr 5, 2021 at 16:27
  • @Jeroen1000 you can simplify that to timestamp=$(grep -Eo '.{18}$' | cut -d '.' -f1 |sed 's/.\{12\}/&./' <<<"file") to use the exact same approach you had, but this time it will work and won't depend on ls.
    – terdon
    Apr 5, 2021 at 16:41
0

@terdon no thank you for your kind solution. For the other linux beginners out there. I had some time zone issue as well so the final result is:

for file in *; do
     timestamp=$(sed -E 's/.*(.{12})(..)\.[^.]*$/\1.\2/') <<<"$file"
     TZ=UTC-2 touch -a -m -t "$timestamp" -- "$file"
done

ps: I use this site to analyse a regex: https://regex101.com/

1
  • If you think that terdon's answer was the most helpful one, then you may accept that answer. Also note that the site that you link to does not support POSIX Basic Regular Expressions (BREs), but a few other dialects of regular expressions used in specific languages, none of which is a Unix shell. This means that expressions that the site says does the right thing may not work as expected (or at all) with standard Unix tools like sed.
    – Kusalananda
    Apr 5, 2021 at 16:42

You must log in to answer this question.

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