Adding a column with awk - bash

I want to add a new column at the beggining of every row. Command used:
tree -afispugD --inodes
I want to put a new column which will be the name of the file.
Example:
119801433 -rwxr--r-- u1915811 alum 1252 Apr 1 21:06 ./file
119802284 -rw-r--r-- u1915811 alum 1255 Mar 20 10:25 ./text.txt
119865862 drwxr-xr-x u1915811 alum 4096 Feb 27 10:20 ./S3/folder2
To this:
file 119801433 -rwxr--r-- u1915811 alum 1252 Apr 1 21:06 ./file
text.txt 119802284 -rw-r--r-- u1915811 alum 1255 Mar 20 10:25 ./text.txt
folder2 119865862 drwxr-xr-x u1915811 alum 4096 Feb 27 10:20 ./S3/folder2
PS: I have to do it because tree command doesn't show names :(

All you need is:
$ awk -F'/' '{print $NF,$0}' file
file 119801433 -rwxr--r-- u1915811 alum 1252 Apr 1 21:06 ./file
text.txt 119802284 -rw-r--r-- u1915811 alum 1255 Mar 20 10:25 ./text.txt
folder2 119865862 drwxr-xr-x u1915811 alum 4096 Feb 27 10:20 ./S3/folder2
or if you want to use some specific spacing in the output use printf instead of print:
$ awk -F'/' '{printf "%-10s%s\n",$NF,$0}' file
file 119801433 -rwxr--r-- u1915811 alum 1252 Apr 1 21:06 ./file
text.txt 119802284 -rw-r--r-- u1915811 alum 1255 Mar 20 10:25 ./text.txt
folder2 119865862 drwxr-xr-x u1915811 alum 4096 Feb 27 10:20 ./S3/folder2
or, since this is a simple substitution on a single line, you could use sed instead of awk:
$ sed 's/\(.*\/\(.*\)\)/\2 \1/' file
file 119801433 -rwxr--r-- u1915811 alum 1252 Apr 1 21:06 ./file
text.txt 119802284 -rw-r--r-- u1915811 alum 1255 Mar 20 10:25 ./text.txt
folder2 119865862 drwxr-xr-x u1915811 alum 4096 Feb 27 10:20 ./S3/folder2

This one will also work, if file has whitespaces in its name or if it's a symbolic link
tree -afispugD --inodes | awk '{FS="./"; ORS=""; printf("%-60s%s\n",$NF,$0)}'

Until there are spaces in the filenames, this should work:
tree -afispugD --inodes | awk '{printf("-30s%s\n",$NF,$0}'

Related

grep only files generated in a particular hour

I am trying to grep some pattern in a file set under a folder like below
Where on the output I have to perform remaining operation.
The output main.log is coming so huge almost 50k lines ,as the files starting with server02.log are almost 30 to 40 in number . The script based on this output is taking forever to complete.
Is there a way that I can only take files name starting with server02.log. and generated between time
20:00:00 and 21:00:00
ls -lrth server02.log.*
-rw-r--r-- 1 user user 1.9M Apr 15 20:20 server02.log.2020
-rw-r--r-- 1 user user 1.7M Apr 15 20:30 server02.log.2030
-rw-r--r-- 1 user user 1.6M Apr 15 20:41 server02.log.2041
-rw-r--r-- 1 user user 1.9M Apr 15 20:50 server02.log.2050
-rw-r--r-- 1 user user 2.1M Apr 15 21:00 server02.log.2100
-rw-r--r-- 1 user user 1.4M Apr 15 21:10 server02.log.2110
-rw-r--r-- 1 user user 1.9M Apr 15 21:20 server02.log.2120
-rw-r--r-- 1 user user 656K Apr 15 21:29 server02.log.2129
-rw-r--r-- 1 user user 4.6M Apr 15 21:40 server02.log.2140
-rw-r--r-- 1 user user 1.9M Apr 15 21:50 server02.log.2150
-rw-r--r-- 1 user user 1.7M Apr 15 21:59 server02.log.2159
-rw-r--r-- 1 user user 724K Apr 15 22:09 server02.log.2209
-rw-r--r-- 1 user user 1.3M Apr 15 22:20 server02.log.2220
-rw-r--r-- 1 user user 1.1M Apr 15 22:29 server02.log.2229
-rw-r--r-- 1 user user 1.7M Apr 15 22:41 server02.log.2241
-rw-r--r-- 1 user user 1.5M Apr 15 22:49 server02.log.2249
-rw-r--r-- 1 user user 2.4M Apr 15 23:01 server02.log.2301
-rw-r--r-- 1 user user 1.4M Apr 15 23:10 server02.log.2310
-rw-r--r-- 1 user user 585K Apr 15 23:19 server02.log.2319
-rw-r--r-- 1 user user 858K Apr 15 23:30 server02.log.2330
-rw-r--r-- 1 user user 892K Apr 15 23:40 server02.log.2340
-rw-r--r-- 1 user user 698K Apr 15 23:49 server02.log.2349
grep -E "###Update |###Initiate |###Re-Initiate " server02.log.* >> main.log
from the comments I made the change to my code as below
#!/bin/bash
DIR="."
d=$(date +%Y-%m-%d);
log_dir="logs/$d"
PREFIX="$log_dir/srv_02.log"
#PREFIX="srv_02.log"
echo "prefix value is $PREFIX"
START_HOUR="06"
for F in "$( find "$DIR" -name "${PREFIX}*" -printf '%Tc %p\n' | grep "\ ${START_HOUR}:" )"; do
echo "F value is $F"
grep -E "###Update |###Initiate |###Re-Initiate" "$F" >> main.log
done
error:
prefix value is logs/2021-04-16/srv_02.log
find: warning: Unix filenames usually don't contain slashes (though pathnames do). That means that '-name `logs/2021-04-16/srv_02.log*'' will probably evaluate to false all the time on this system. You might find the '-wholename' test more useful, or perhaps '-samefile'. Alternatively, if you are using GNU grep, you could use 'find ... -print0 | grep -FzZ `logs/2021-04-16/osbpd_srv_02.log*''.
F value is
grep: : No such file or directory
This solution looks for files in the given directory, created during the specified hour with names matching the given prefix.
#!/bin/bash
d=$(date +%Y-%m-%d)
DIR="logs/$d/$log_dir"
PREFIX="srv_02.log"
#PREFIX=server02.log
echo "prefix value is $PREFIX"
START_HOUR="06"
for F in "$( find "$DIR" -name "${PREFIX}*" -printf '%TY-%Tm-%Td\n' | grep "\ ${START_HOUR}:" )"; do
echo "$F"
# grep -E "###Update |###Initiate |###Re-Initiate Assignment Milestone|###Complete Assignment Milestone|###Cancel Assignment Milestone|###Suspend Assignment Milestone|###Resume Assignment Milestone" "$F" >> main.log
done

How to display number of items in a folder - Bash

When I execute the ls -l -h command, I get an output as show by the image below.
How can the number of the items in a folder be included in the output?
Update
The current output looks like this
total 41M
-rw-r--r-- 1 root root 41M Dec 20 09:56 completed_projects.bson
-rw-r--r-- 1 root root 213 Dec 20 09:57 completed_projects.metadata.json
drwxrwxr-x 2 adipster adipster 4.0K Jun 16 13:22 contents
-rw-rw-r-- 1 adipster adipster 13 Jun 16 13:20 file.py
drwxrwxr-x 4 adipster adipster 4.0K Jun 16 13:22 folder
drwxrwxr-x 2 adipster adipster 4.0K Jun 16 13:21 items
But I'll like to have another column indicating the number of items in a folder like this
total 41M
-rw-r--r-- 1 root root 41M Dec 20 09:56 completed_projects.bson
-rw-r--r-- 1 root root 213 Dec 20 09:57 completed_projects.metadata.json
drwxrwxr-x 2 adipster adipster 4.0K Jun 16 13:22 contents 235
-rw-rw-r-- 1 adipster adipster 13 Jun 16 13:20 file.py
drwxrwxr-x 4 adipster adipster 4.0K Jun 16 13:22 folder 19
drwxrwxr-x 2 adipster adipster 4.0K Jun 16 13:21 items 5
where the numbers at the extreme right represents the number of items in a folder
You can do something like this:
echo -n "Number of files in folder is: " && ls | wc -l && ls -l
ouptut should be something like this:
umber of files in folder is: 3
Total 279K
-rwxr-xr-x 1 user users 19K Jun 16 00:17 a
-rwxr-xr-x 1 user users 5K Jun 16 00:17 b
-rwxr-xr-x 1 user users 255K Jun 16 00:17 c
You can omit echo statement, just as a note -n is no new line flag.
sed has an option to execute the constructed replacement with /e.
We only count subdirs, looking at the first character
ls -l | sed -r 's/d(.*) ([^ ]*)/printf "d%s %-20s%s\n" "\1" \2 $(ls \2| wc -l)/e'
EDIT: Solution for directories with spaces in their name.
Parsing ls should be avoided. When you try to fix above cmmand for directory names with spaces, you might try
# Don't do this
ls -l | sed -r 's/d(.{,48}) (.*)/printf "d%s %-20s%s\n" "\1" "\2" $(ls "\2"| wc -l)/e'
It is time to write a script. Perhaps with find or something like
#/bin/bash
for i in *; do
printf "%-70s %s\n" "$(/bin/ls -ld "$i")" "$(/bin/ls -d "$i"/* 2>/dev/null| wc -l)"
done
The wc in the subdir will count wrong when filenames have newlines.
ls() { command ls "$#" | tee >(echo "$(wc -l) items"); }
That uses an output process substitution to run the little "echo" script on its stdin while also displaying stdin (thanks to tee). This way, you don't have to run ls twice.
Usual caveat: output will be incorrect when there's a file with a newline in the name.

Can I change the modified date, just the year, of a file or all files?

Can I change the modified date, just the year, of a file or all files?
I have been looking here which lead me to touch.
$ ls -l *.txt
-rw-r--r-- 1 kevin.smith mkpasswd 3319 Nov 21 2017 adjectives.txt
-rw-r--r-- 1 kevin.smith mkpasswd 25562 Aug 11 2015 checklist.txt
-rwxr-xr-x 1 kevin.smith mkpasswd 11347 May 9 14:28 cw_text.txt
-rw-r--r-- 1 kevin.smith mkpasswd 9260 May 9 14:31 cw_text2.txt
-rw-r--r-- 1 kevin.smith mkpasswd 4786 May 9 14:38 cw_text3.txt
-rw-r--r-- 1 kevin.smith mkpasswd 390 Jun 25 2014 Delete_log.txt
-rw-r--r-- 1 kevin.smith mkpasswd 6891 Jul 27 2015 log.txt
-rw-r--r-- 1 kevin.smith mkpasswd 53828 Jan 17 2017 pin1.txt
-rw-r--r-- 1 kevin.smith mkpasswd 39412 Jan 17 2017 pip2.txt
-rw-r--r-- 1 kevin.smith mkpasswd 167 Dec 5 2015 romeo.txt
$ touch -t 2018* *.txt
Expected Output: Would have only the year changed to 2018
$ ls -l *.txt
-rw-r--r-- 1 kevin.smith mkpasswd 3319 Nov 21 2018 adjectives.txt
-rw-r--r-- 1 kevin.smith mkpasswd 25562 Aug 11 2018 checklist.txt
-rwxr-xr-x 1 kevin.smith mkpasswd 11347 May 9 14:28 cw_text.txt
-rw-r--r-- 1 kevin.smith mkpasswd 9260 May 9 14:31 cw_text2.txt
-rw-r--r-- 1 kevin.smith mkpasswd 4786 May 9 14:38 cw_text3.txt
-rw-r--r-- 1 kevin.smith mkpasswd 390 Jun 25 2018 Delete_log.txt
-rw-r--r-- 1 kevin.smith mkpasswd 6891 Jul 27 2018 log.txt
-rw-r--r-- 1 kevin.smith mkpasswd 53828 Jan 17 2018 pin1.txt
-rw-r--r-- 1 kevin.smith mkpasswd 39412 Jan 17 2018 pip2.txt
-rw-r--r-- 1 kevin.smith mkpasswd 167 Dec 5 2018 romeo.txt
ls -l file.txt; touch -t "$(date -d "#$(stat -c '%Y' file.txt)" "+2020%m%d%H%M")" file.txt; ls -l file.txt
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-rw-r--r-- 1 jackman jackman 0 Oct 23 16:47 file.txt
-rw-r--r-- 1 jackman jackman 0 Oct 23 2020 file.txt
You'll need to use a for loop to iterate over the files, and query/update the mtime one-by-one

Extract lines with duplicate last field

Based on a list generated with the command
find '/patch' -name A* -exec ls -la {} \;
Get only a list with file names that appears more than once (duplicated), but I want display the full line not only the file name.
File name consider only the name of file not the full path.
Example
Based on this file:
-rw-r--r-- 1 root root 34K Jan 9 2014 /usr/share/dia/sheets/AADL.sheet
-rw-r--r-- 1 root root 952 Fev 14 07:07 /usr/share/postgresql/9.3/man/man7/ABORT.7.gz
-rw-r--r-- 1 root root 955 Jul 30 2014 /usr/share/postgresql/9.1/man/man7/ABORT.7.gz
-rw-r--r-- 1 root root 92K Abr 28 2014 /usr/share/gettext/ABOUT-NLS
-rw-r--r-- 1 root root 545 Dez 14 2013 /usr/share/dia/sheets/Automata.sheet
-rw-r--r-- 1 root root 6,7K Dez 21 2012 /usr/share/perl5/Mail/Address.pm
-rw-r--r-- 1 root root 709 Mar 3 09:03 /home/test/Address.pm
-rw-r--r-- 1 root root 709 Mar 3 11:13 /home/test/Automata.sheet
-rw-r--r-- 1 root root 520 Mar 3 11:15 /home/test/t2/Address.pm
I want get this result:
-rw-r--r-- 1 root root 952 Fev 14 07:07 /usr/share/postgresql/9.3/man/man7/ABORT.7.gz
-rw-r--r-- 1 root root 955 Jul 30 2014 /usr/share/postgresql/9.1/man/man7/ABORT.7.gz
-rw-r--r-- 1 root root 6,7K Dez 21 2012 /usr/share/perl5/Mail/Address.pm
-rw-r--r-- 1 root root 709 Mar 3 09:03 /home/test/Address.pm
-rw-r--r-- 1 root root 520 Mar 3 11:15 /home/test/t2/Address.pm
-rw-r--r-- 1 root root 545 Dez 14 2013 /usr/share/dia/sheets/Automata.sheet
-rw-r--r-- 1 root root 709 Mar 3 11:13 /home/test/Automata.sheet
Using this commands
awk -F. '{ n = split($0, a, "/"); print a[n] }' file |sort | uniq -d > filedups
I got
ABORT.7.gz
Address.pm
Automata.sheet
and after
grep -f filedups file
I get expected result.
My question:
Is there a direct way to do this in just one line using awk and/or other commands?
awk to the rescue!
starting with your initial file
$ awk '{n=split($NF,a,"/"); k=a[n]; c[k]++;
v[k]=k in v?v[k] ORS $0:$0}
END {for(k in c) if(c[k]>1) print v[k]}' file
-rw-r--r-- 1 root root 6,7K Dez 21 2012 /usr/share/perl5/Mail/Address.pm
-rw-r--r-- 1 root root 709 Mar 3 09:03 /home/test/Address.pm
-rw-r--r-- 1 root root 520 Mar 3 11:15 /home/test/t2/Address.pm
-rw-r--r-- 1 root root 545 Dez 14 2013 /usr/share/dia/sheets/Automata.sheet
-rw-r--r-- 1 root root 709 Mar 3 11:13 /home/test/Automata.sheet
-rw-r--r-- 1 root root 952 Fev 14 07:07 /usr/share/postgresql/9.3/man/man7/ABORT.7.gz
-rw-r--r-- 1 root root 955 Jul 30 2014 /usr/share/postgresql/9.1/man/man7/ABORT.7.gz

select fields with sed

Using only grep and sed, is there a way I can tranform the output of ls -l * into this :
-rw-r--r-- agenda.txt
-rw-r--r-- annuaire.txt
Thanks!
seeing that you have already got your "answer", here's one of the simpler solution
ls -l | tr -s " "| cut -d" " -f1,8-
#OP, sed is "powerful", but sometimes, simplicity is more powerful.
Side note: Don't parse file names like that.
ls -l | sed 's/[ ]+//g' | sed 's/ [0-9].*:.[0-9]/ /g'
ls -altrh| sed -E 's/ +.+ / / g'
Or you can go with ssed which supports Perl Regular Expressions.
I solved your problem using the ssed program you can install it in any Posix system, ssed stands for super sed.
so i did a ls -latrh in my home directory.
telsa:~ mahmoh$ ls -altrh
total 136
drwxr-xr-x 5 root admin 170B Jun 24 00:27 ../
drwx------+ 4 mahmoh staff 136B Jun 24 00:27 Pictures/
drwx------+ 3 mahmoh staff 102B Jun 24 00:27 Music/
drwx------+ 3 mahmoh staff 102B Jun 24 00:27 Movies/
drwx------+ 3 mahmoh staff 102B Jun 24 00:27 Desktop/
-rw------- 1 mahmoh staff 3B Jun 24 00:27 .CFUserTextEncoding
drwxr-xr-x+ 5 mahmoh staff 170B Jun 24 00:27 Public/
drwx------+ 5 mahmoh staff 170B Jun 24 02:19 Documents/
-rw-r--r--# 1 mahmoh staff 15K Jun 24 02:19 .DS_Store
drwx------# 36 mahmoh staff 1.2K Jun 24 14:48 Library/
-rw-r--r-- 1 mahmoh staff 279B Jun 24 15:27 .profile~
-rw-r--r--# 1 mahmoh staff 14K Jun 24 15:29 .vimrc
-rw-r--r-- 1 mahmoh staff 279B Jun 24 15:30 .profile
drwx------ 2 mahmoh staff 68B Jun 24 15:46 .Trash/
drwxr-xr-x 3 mahmoh staff 102B Jun 24 20:26 .mplayer/
-rw------- 1 mahmoh staff 3.5K Jun 24 22:11 .bash_history
-rw------- 1 mahmoh staff 42B Jun 24 23:25 .lesshst
-rw-r--r-- 1 mahmoh staff 3.6K Jun 24 23:39 temp
-rw-r--r-- 1 mahmoh staff 3.3K Jun 24 23:43 rtorrent.rc~
drwxr-xr-x 5 mahmoh staff 170B Jun 24 23:52 torrents/
-rw-r--r-- 1 mahmoh staff 3.3K Jun 24 23:56 .rtorrent.rc~
-rw------- 1 mahmoh staff 3.7K Jun 24 23:56 .viminfo
-rw-r--r-- 1 mahmoh staff 3.3K Jun 24 23:56 .rtorrent.rc
drwxr-xr-x+ 25 mahmoh staff 850B Jun 24 23:56 ./
drwx------+ 10 mahmoh staff 340B Jun 24 23:58 Downloads/
Now watch.
telsa:~ mahmoh$ ls -altrh| ssed -R -e 's/ +.+ / / g'
total 136
drwxr-xr-x ../
drwx------+ Pictures/
drwx------+ Music/
drwx------+ Movies/
drwx------+ Desktop/
-rw------- .CFUserTextEncoding
drwxr-xr-x+ Public/
drwx------+ Documents/
-rw-r--r--# .DS_Store
drwx------# Library/
-rw-r--r-- .profile~
-rw-r--r--# .vimrc
-rw-r--r-- .profile
drwx------ .Trash/
drwxr-xr-x .mplayer/
-rw------- .bash_history
-rw------- .lesshst
-rw-r--r-- temp
-rw-r--r-- rtorrent.rc~
drwxr-xr-x torrents/
-rw-r--r-- .rtorrent.rc~
-rw------- .viminfo
-rw-r--r-- .rtorrent.rc
drwxr-xr-x+ ./
drwx------+ Downloads/
ls -l | sed 's/^\([^\t ]\+\)\(.*:.[^ \t]\+\)\(.\+\)/\1 \3/'
Here is a working command. The slightly tricky thing is that ls -l will print the year for files that are older than some time (6 months) and hh:mm for newer files.
ls -l | sed 's/ .*[0-9]* .*[A-Z][a-z][a-z] [ 0-9][0-9] \{1,2\}[0-9][0-9]:*[0-9][0-9] / /'
For the following example
drwxr-xr-x 39 root root 1024 Feb 19 08:58 /
the starting .* will match 39 root root 1024 and then the rest of the regular expression matches month name (so you might restrict a-z to fewer characters) followed by year or hh:mm.
why not use awk instead of sed? awk is built for stuff like this.
see this manual page for more about fields in awk.
Like this?
ls -l | sed 's/ [0-9].*:.[0-9] / /' | less
Transforms
-rw-r--r-- 1 tomislav tomislav 609 2009-11-26 10:32 Test.class
-rw-r--r-- 1 tomislav tomislav 46 2009-12-14 12:16 test.groovy
into
-rw-r--r-- Test.class
-rw-r--r-- test.groovy

Resources