1

This is a self answered question for something others also might find useful.

In Dolphin, I sometimes switch between sorting by name and sorting by modification date. However, it can be annoying since Dolphin does not change the order for only the folder I'm in. It changes for everything. So I wonder how I can fix the modification date to match the name order, so that a particular directory is unaffected when switching between name and date in Dolphin.

3 Answers 3

1

Turns out it's extremely easy.

for FILE in *; do touch -- "$FILE"; sleep 0.001; done # sort

And if you want to do it recursively:

for FILE in **/*; do touch -- "$FILE"; sleep 0.001; done # sort

The # sort is only a comment. The purpose is to make it easy to search backwards for the command.

5
  • 1
    * is not equivalent to ls. * has the following semantics: unix.stackexchange.com/a/368325/43400, it sorts based sorting order in the current locale
    – Ferrybig
    Jun 25, 2023 at 14:05
  • Yep, that has nothing to do with ls, and would work in a system with no ls at all! And at least in zsh you can also set the ordering using the extended glob syntax :) Jun 25, 2023 at 17:05
  • Thanks, removed that
    – klutt
    Jun 25, 2023 at 17:38
  • @Ferrybig, so does ls. Jun 26, 2023 at 7:25
  • @MarcusMüller, glob qualifiers in zsh don't require extendedglob (unless you also turn off bareglobqual after which glob qualifiers can still be expressed with the (#q...) extendedglob operator). Jun 26, 2023 at 7:28
1

Your approach (especially the one before you added the sleep 0.001 call) only works on systems and file systems where time stamps have subsecond precision, and even then, on some, like on Linux with ext4 file systems, even though timestamps have nanosecond resolution, you'll find that some batches of files will have the same timestamp (see linux: touch date precision and Why is filesystem time always some msecs behind system time in Linux? for details).

Here after a:

for f ({1..20}) touch $f
$ ls --full-time -lrt
total 0
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.183520078 +0100 2
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.183520078 +0100 1
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.187520223 +0100 3
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 6
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 5
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.191520368 +0100 4
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.195520512 +0100 8
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.195520512 +0100 7
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.199520657 +0100 9
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.199520657 +0100 10
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 13
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 12
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.203520802 +0100 11
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.207520947 +0100 15
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.207520947 +0100 14
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.211521092 +0100 17
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.211521092 +0100 16
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.215521237 +0100 19
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.215521237 +0100 18
-rw-r--r-- 1 chazelas chazelas 0 2023-06-26 07:41:04.219521381 +0100 20

(and even a sleep 0.001 may not help, touch already takes over 1ms to run on my system as you can tell from those timestamps above).

It's better with touch -d '0.000000001 second ago' (assuming GNU touch):

$ strace -qqe /utime touch -d now a
utimensat(0, NULL, NULL, 0)             = 0
$ strace -qqe /utime touch -d '0.000000001 second ago' a
utimensat(0, NULL, [{tv_sec=1687765504, tv_nsec=213757695} /* 2023-06-26T08:45:04.213757695+0100 */, {tv_sec=1687765504, tv_nsec=213757695} /* 2023-06-26T08:45:04.213757695+0100 */], 0) = 0

Where touch requests an explicit timestamp which it computes using a full precision clock time request to the system as opposed to letting the system pick the current cached fs time, so you're almost guaranteed that two subsequent calls will get a different timestamp, but that would still not work wherever timestamps don't support subsecond precision.

If using zsh (and with GNU touch or compatible), you could do:

zmodload zsh/datetime
now=$EPOCHSECONDS
for file ( *(NOn) ) touch -d @$((now--)) -- $file

Or with standard touch:

zmodload zsh/datetime
now=$EPOCHSECONDS
for file ( *(NOn) ) {
  TZ=UTC0 strftime -s t %Y%m%d%H%M.%S $((now--)) &&
    TZ=UTC0 touch -t $t -- $file
}

Where $EPOCHSECONDS is the current Unix epoch time and *(NOn) is like * but with NULLGLOB on and with the files Ordered by name in reverse (captial O to reverse) so you get the same order as in ls -r. Then we make the timestamps one second apart going back in time.

-1

As others have already stated ls and (echo) * sort differently.

If you want to sort like ls (or ls -[some option], i.e ls -r sorts you could simply use it instead of *.

Also notice that "*" is inherently unsafe, because it expands to a list of all filenames which can (in large directories) bring the resulting command line over the line length limit. The shell (depending on shell, version, ...) will react with an error like "command line too long".

Finally, as an aside: it is a safe habit not to name shell variables all uppercase. System variables (i.e. "PATH", "SHELL", "OPTARG", ...) are usually all uppercase and if you name your own variables the same you run the risk of (un-)setting such a variable. Use mixed- (or lower-) case for your own scripts and you are on the safe side.

ls |\
while read fFile ; do
     touch fFile
done
4
  • 1
    * is meant to sort the files in the same order as ls does Jun 26, 2023 at 6:37
  • The argument list too long error is a limit of the execve() system call, not the shell when an external command is executed. In the OP's approach, the only command that is executed is touch and it takes only one file like in your approach. Jun 26, 2023 at 6:39
  • The syntax to read a line is IFS= read -r line, not read line. But in any case, file names are not lines, they're sequences of bytes other than 0 and the one representing /. And that included the line delimiter Jun 26, 2023 at 6:40
  • To use ls, you'd need GNU ls >= 9.0, zsh or bash and ls --zero | while IFS= read -rd '' file; do touch -- "$file"; done ("$file", not $file in bash, let alone file). Jun 26, 2023 at 6:50

You must log in to answer this question.

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