My two cents
The asker wanted to parse output of ls -ls
Below is what I get when I run ls -ls
command from the console.
total 40
36 -rwxrwxr-x 1 amit amit 36720 2012-03-31 12:19 1.txt
4 -rwxrwxr-x 1 amit amit 1318 2012-03-31 14:49 2.txt
But there are few answer addressing this parsing operation.
ls
's output
Before trying to parse something, we have to ensure command output is consistant, stable and easy to parse as possible
- In order to ensure output wont be altered by some
alias
you may prefer to specify full path of command: /bin/ls
.
- Avoid variations of output due to locales, prefix your command by
LANG=C LC_ALL=C
- Use
--time-style
command switch to use UNIX EPOCH more easier to parse time infos.
- Use
-b
switch for holding special characters
So we will prefer
LANG=C LC_ALL=C /bin/ls -lsb --time-style='+%s.%N'
to just
ls -ls
Full bash sample
#!/bin/bash
declare -a bydate=() bysize=() byname=() details=()
declare -i cnt=0 vtotblk=0 totblk
{
read -r _ totblk # ignore 1st line
while read -r blk perm lnk usr grp sze date file;do
byname[cnt]="${file//\\ / }"
details[cnt]="$blk $perm $lnk $usr $grp $sze $date"
bysize[sze]+="$cnt "
bydate[${date/.}]+="$cnt "
cnt+=1 vtotblk+=blk
done
} < <(LANG=C LC_ALL=C /bin/ls -lsb --time-style='+%s.%N')
From there, you could easily sort by dates, sizes of names (sorted by ls
command).
echo "Path '$PWD': Total: $vtotblk, sorted by dates"
for dte in ${!bydate[@]};do
printf -v msec %.3f .${dte: -9}
for idx in ${bydate[dte]};do
read -r blk perm lnk usr grp sze date <<<"${details[idx]}"
printf ' %11d %(%a %d %b %T)T%s %s\n' \
$sze "${date%.*}" ${msec#0} "${byname[idx]}"
done
done
echo "Path '$PWD': Total: $vtotblk, sorted by sizes"
for sze in ${!bysize[@]};do
for idx in ${bysize[sze]};do
read -r blk perm lnk usr grp sze date <<<"${details[idx]}"
printf -v msec %.3f .${date#*.}
printf ' %11d %(%a %d %b %T)T%s %s\n' \
$sze "${date%.*}" ${msec#0} "${byname[idx]}"
done
done
echo "Path '$PWD': Total: $vtotblk, sorted by names"
for((idx=0;idx<cnt;idx++));{
read -r blk perm lnk usr grp sze date <<<"${details[idx]}"
printf -v msec %.3f .${date#*.}
printf ' %11d %(%a %d %b %T)T%s %s\n' \
$sze "${date%.*}" ${msec#0} "${byname[idx]}"
}
( Accessory, you could check if total block printed by ls
match total block by lines:
(( vtotblk == totblk )) ||
echo "WARN: Total blocks: $totblk != Block count: $vtotblk" >&2
Of course, this could be inserted before first echo "Path...
;)
Here is an output sample. (Note: there is a filename with a newline)
Path '/tmp/so': Total: 16, sorted by dates
0 Sun 04 Sep 10:09:18.221 2.txt
247 Mon 05 Sep 09:11:50.322 Filename with\nsp\303\251cials characters
13 Mon 05 Sep 10:12:24.859 1.txt
1313 Mon 05 Sep 11:01:00.855 parseLs.00
1913 Thu 08 Sep 08:20:20.836 parseLs
Path '/tmp/so': Total: 16, sorted by sizes
0 Sun 04 Sep 10:09:18.221 2.txt
13 Mon 05 Sep 10:12:24.859 1.txt
247 Mon 05 Sep 09:11:50.322 Filename with\nsp\303\251cials characters
1313 Mon 05 Sep 11:01:00.855 parseLs.00
1913 Thu 08 Sep 08:20:20.836 parseLs
Path '/tmp/so': Total: 16, sorted by names
13 Mon 05 Sep 10:12:24.859 1.txt
0 Sun 04 Sep 10:09:18.221 2.txt
247 Mon 05 Sep 09:11:50.322 Filename with\nsp\303\251cials characters
1913 Thu 08 Sep 08:20:20.836 parseLs
1313 Mon 05 Sep 11:01:00.855 parseLs.00
And if you want to format characters (with care: there could be some issues, if you don't know who create content of path
). But if folder is your, you could:
echo "Path '$PWD': Total: $vtotblk, sorted by dates, with special chars"
printf -v spaces '%*s' 37 ''
for dte in ${!bydate[@]};do
printf -v msec %.3f .${dte: -9}
for idx in ${bydate[dte]};do
read -r blk perm lnk usr grp sze date <<<"${details[idx]}"
printf ' %11d %(%a %d %b %T)T%s %b\n' $sze \
"${date%.*}" ${msec#0} "${byname[idx]//\\n/\\n$spaces}"
done
done
Could output:
Path '/tmp/so': Total: 16, sorted by dates, with special chars
0 Sun 04 Sep 10:09:18.221 2.txt
247 Mon 05 Sep 09:11:50.322 Filename with
spécials characters
13 Mon 05 Sep 10:12:24.859 1.txt
1313 Mon 05 Sep 11:01:00.855 parseLs.00
1913 Thu 08 Sep 08:20:20.836 parseLs