grep ls output across tabs - bash

If I ls -l in a directory and get:
-rwxr-x--- 1 user1 admin 0 8 Aug 2012 file.txt
-rwxr-x--- 1 user1 admin 1733480 26 Jul 2012 Archive.pax.gz
drwxr-x---# 7 user1 admin 238 31 Jul 2012 Mac Shots
-rwxr-x---# 1 user3 admin 598445 31 Jul 2012 Mac Shots.zip
-rwxr-x---# 1 user1 admin 380 6 Jul 2012 an.sh
-rwxr-x--- 1 user2 admin 14 30 Jun 2012 analystName.txt
-rwxr-x--- 1 user1 admin 36 8 Aug 2012 apple.txt
drwxr-x---# 7 user1 admin 238 31 Jul 2012 iPad Shots
-rwxr-x---# 1 user1 admin 7372367 31 Jul 2012 iPad Shots.zip
-rwxr-x--- 1 user2 admin 109 30 Jun 2012 test.txt
drwxr-x--- 3 user1 admin 102 26 Jul 2012 usr
but want to list only the files owned by "user1" which were modified in "Aug" to get
-rwxr-x--- 1 user1 admin 0 8 Aug 2012 file.txt
-rwxr-x--- 1 user1 admin 36 8 Aug 2012 apple.txt
What is the best method?

Parsing ls output is never a good and reliable solution. ls is a tool for interactively looking at file information. Its output is formatted for humans and will cause bugs in scripts. Use globs or find instead. Understand why: http://mywiki.wooledge.org/ParsingLs
Instead, you can try :
find . -type f -user 'user1' -maxdepth 1
or
find . -type f -printf '%u %f\n' -maxdepth 1 # if you want to show the username
or
stat -c '%U %f' * | cut -d" " -f2-
See
man find
man stat

Or you can be more explicit, since Michael's grep would also find a file owned by user1 namedd 'August iPad Shots' no matter when it was modified:
ls -l | awk '($3=="user1" && $7=="Aug")'

I think the safest way to do it is like this :
touch --date "2012-08-01" /tmp/start
touch --date "2012-09-01" /tmp/stop
find . -maxdepth 1 -type f -user user1 -newer /tmp/start -not -newer /tmp/stop -print0 | xargs -0 ls -l {}
rm /tmp/start /tmp/stop
Or as a one liner
touch --date "2012-08-01" /tmp/start; touch --date "2012-09-01" /tmp/stop; find . -maxdepth 1 -type f -user user1 -newer /tmp/start -not -newer /tmp/stop -print0 | xargs -0 ls -l {}; rm /tmp/start /tmp/stop
Advantages:
You don't parse ls
It works for filenames with Aug in them
Disadvantages
It is a bit long
Explanation:
-maxdepth 1: restricts the results to the current directory
-type f: restricts the results to files
-user user1: resttrings the results to files that belong to user1
-newer /tmp/start: restring the results to files newer than /tmp/start, which was created with the desired date
-not -newer /tmp/stop: restring the results to files not newer than /tmp/stop, which was created with the desired date
-print0: so it can handle filenames with newlines in their name!

How about ls -l | grep user1 | grep Aug?
Or you can combine the regexp: ls -l | grep 'user1.*Aug'

Related

Listing directory with hiden files first and grouping directories

I'm trying to compose a command/function to list any directory displaying the files with the following rules, from top level to deepest level:
hidden files
directories
alphabetically
Another feature I want to grant is to be able to use the command/function for a passed directory. Example:
Goal
Basically I'd like to input something like:
user#machine:cuur-path $ my_listing_magic /some/crazy/directory
and get an output looking like:
drwxrwxr-x 6 User Group 4,0K abr 8 12:12 .aaa_dir/
drwxrwxr-x 3 User Group 4,0K abr 8 12:12 .bbb_dir/
drwxrwxr-x 3 User Group 4,0K out 14 2020 .ccc_dir/
-rw-rw-r-- 1 User Group 4,1K abr 12 17:44 .a_file
-rw-r--r-- 1 User Group 25 dez 4 2017 .b_file
-rw-rw-r-- 1 User Group 21 mai 20 15:50 .c_file
drwx------ 44 User Group 4,0K mai 27 16:44 ddd_dir/
drwx------ 3 User Group 4,0K abr 5 2018 eee_dir/
drwx------ 3 User Group 4,0K abr 5 2018 fff_dir/
-rw-r--r-- 1 User Group 4,8K jun 1 18:52 d_file
-rw-r--r-- 1 User Group 1,2K jun 1 19:42 e_file
-rw------- 1 User Group 106K jun 1 19:33 f_file
(note that the directories . and .. are not there.)
Failed Atempts
Just use ls options
The closest I've gotten is ls -alhvF --group-directories-first. It is almost there, but doesn't respect the desired order and keeps both . and ... I coulld strip those out a posteriori, but since the ordering is not correct, there is no point.
ls -lv namely the -v is nice as it sorts all the .files/dirs up alphabetical.
ls -l --goups-directories-first is also cool, does what is expected. To bad I can't make it 'act before' the -v.
Divide and Conquer
I though of splitting up the problem into two: I could first list the hidden files and then the not hidden: list_hidden; list_non_hiden. Then for simplicity of usage I could create an alias or function to call them in proper order.
The non-hidden is quite trivial! But listing solely the hidden files is becoming a bit more troublesome. I tried something like ls -ld .!(|.), but this has the drawback of using -d - so it cannot be used to list some other directory. At least not without having the full path of the file there...
Thanks!
The ls utility does not support what you want. So implement it yourself. List the files, then sort them, then ls them, yourself. A script that uses zero-terminated stream and GNU coreutils and findutils could look like the following:
{
printf "%s\0" . ..;
cmd=(
find "${1:-.}" -mindepth 1 -maxdepth 1 '('
-name '.*' '('
-type d -printf "1 %p\0"
')' -o
-printf "2 %p\0"
')' -o '('
'!' -name '.*' '('
-type d -printf "3 %p\0"
')' -o
-printf "4 %p\0"
')'
)
"${cmd[#]}" |
sort -z |
cut -z -d' ' -f2-;
} | xargs -0 ls -ald
You can patch some open-source ls utility with your own behavior - it would be nice and I am in favor of implementing it inside GNU coreutils ls as yet another option like --sort-hidden-directories-first to sort them in front of hidden files.
You can also write your own ls utility in C (or you could reimplement ls in shell, but I suspect It would be harder then just writing it in C).
Because I liked the idea for a long time and wanted to have hidden files sorted before anything else, I created this l utility that uses the method described above.

How to get a particular field from ls output

i am trying to get the info of the latest folder created in a particular path.
Here i am using the below command to fetch and filter the results so that i get only folders starting with 11,12,19:
ls_info=$(ls -lrt /orcl/grid/product |grep '11\|12\|19')
The output of ls_info is :
total 12
drwxrwx--- 3 oragrid oinstall 4096 May 21 2014 11.2.0.3
drwxr-xr-x 3 oragrid oinstall 4096 Feb 25 2019 11.2.0.4
How can i fetch "11.2.0.4" from this,which is the latest created folder.
Please suggest.Thanks.
Do not parse ls. Use find instead. First get the list of directories you want and print the directories with the modification timestamps. Then sort the list, filter newest line and remove the timestamp. With GNU utilities you can:
find /orcl/grid/product -mindepth 1 -maxdepth 1 -type d '(' -name '11*' -o -name '12*' -o -name '19*' ')' -printf "%Ts\t%f\n" | sort -n | cut -f2- | tail -n1

shell alias executes in home directory

I have following line in my .zshrc file:
alias clean="sed -i 's/\r//g; s/ /\t/g' $(find . -maxdepth 1 -type f)"
but when I try to execute it in /path/to/some/directory the output is:
sed ./.Xauthority
./.lesshst: No such file or directory
.Xauthority and .lesshst are both in my home directory.
Substitutiong . with $(pwd) dos not help.
When defining the alias you've used double quotes to encompass the entire (alias) definition. This has the effect of actually running the find command at the time the alias is defined.
So when the alias is created it will pick up a list of files from the directory in which the alias is being defined (eg, in your home directory when sourcing .zshrc).
You can see this happening in the following example:
$ cd /tmp
$ pwd
/tmp
$ ls -l
total 36036
drwxrwxrwt+ 1 myid None 0 Oct 10 11:31 ./
drwxr-xr-x+ 1 myid None 0 Jul 12 17:28 ../
-rw-r--r-- 1 myid Administrators 0 Oct 10 11:31 a
-rw-r--r-- 1 myid Administrators 0 Oct 10 11:31 b
-rw-r--r-- 1 myid Administrators 0 Oct 10 11:31 c
-rw-r--r-- 1 myid Administrators 0 Oct 10 11:31 d
-rw-r--r-- 1 myid Administrators 36864002 Jun 6 17:29 giga.txt
drwx------+ 1 myid Administrators 0 Mar 8 2020 runtime-xward/
$ alias clean="sed -i 's/\r//g; s/ /\t/g' $(find . -maxdepth 1 -type f)"
$ alias clean
alias clean='sed -i '\''s/\r//g; s/ /\t/g'\'' ./a
./b
./c
./d
./giga.txt'
Notice how the find was evaluated at alias definition time and pulled in all of the files in my /tmp directory.
To address this issue you want to make sure the find is not evaluated at the time the alias is created.
There are a few ways to do this, one idea being to wrap the find portion of the definition in single quotes, another idea would be keep the current double quotes and just escape the $, eg:
$ alias clean="sed -i 's/\r//g; s/ /\t/g' "'$(find . -maxdepth 1 -type f)'
$ alias clean
alias clean='sed -i '\''s/\r//g; s/ /\t/g'\'' $(find . -maxdepth 1 -type f)'
$ alias alias clean="sed -i 's/\r//g; s/ /\t/g' \$(find . -maxdepth 1 -type f)"
$ alias clean
alias clean='sed -i '\''s/\r//g; s/ /\t/g'\'' $(find . -maxdepth 1 -type f)'
Notice in both cases the alias contains the actual find command instead of the results of evaluating it in the current directory.

Read first line from latest file

I’m working on requirement where I need to read first line from latest file under a directory. In a directory I can have multiple files but I want to read first line of latest file out all files which are having PPP in their file name.
I know how to read first line of file and write into a file
head -n 1 jsonPPPvp.txt > output.txt
But how can I pick latest file ( as per the time stamp) out of all files in a directory which are having PPP in it..?
Any suggestions please...!
I’ve written a command
Using find with -print0 and xargs -0 in a command substitution
Your optimal solution, though still requiring 4 subshells, will protect against all caveats in filenames by having find output nul-terminated filenames that can be used in conjunction with xargs -0 to form a nul-terminated list of filenames to be passed to ls for sorting in reverse selecting the last file with tail -n1 and the first line in that file with head -n1.
Using the -maxdepth 1 option to find limits the search to the current directory and prevent recursing into subdirectories (remove it if you want to search the entire directory tree below the current directory), e.g.
head -n1 $(find . -maxdepth 1 -type f -name "*PPP*" -print0 |
xargs -0 ls -rt |
tail -n 1)
In addition to working with nul-terminated filenames, it will benefit from letting xargs form the list to sort rather than looping to find the newest.
It is not maybe the best solution but it works (by latest file, I have considered the file modified with the most recent timestamp ):
ls -ltra
total 32
drwxr-xr-x 3 allanrobert primarygroup 4096 Feb 15 17:37 ..
drwxr-xr-x 2 allanrobert primarygroup 4096 Feb 15 17:37 .
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 file2PPP2
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 other
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 file3PPP3
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 other2
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 other1
-rw-r--r-- 1 allanrobert primarygroup 6 Feb 15 17:40 file1PPP
file content:
cat file1PPP
a
b
c
Command:
find . -type f -maxdepth 1 -name '*PPP*' -printf '%T+ %p\n' | sort -r | head -1 | cut -d' ' -f2 | xargs head -1
a
Beware of spaces in filenames!
temp = `ls -Art | head -n 1 `
head -1 $temp
head -n 1 $(find ./ -name "*PPP*" -type f | xargs ls -rt1 | tail -n 1)
The drawback of the command above is that you must have a *PPP* file in your directory, otherwise the command produces wrong result.
You can also try this:
ls -tr | grep "PPP" | tail -n 1 | xargs head -n 1

Error handling for find and remove the directory that returns error even after it is successful

I am writing a shell script to find some old directories to replace them with the latest one. I used the following command. It is successful in deleting but I am running into this error.
-bash-4.1$ find /app/home/data01/dpx/ -type d -mtime +2 -name 'stress' -exec ls -ltr {} \;
total 24
-rw-r--r-- 1 dpx app 1324 Oct 21 2017 relocate1.sh
-rw-r--r-- 1 dpx app 316 Oct 21 2017 re1.sh
-rw-r--r-- 1 dpx app 11876 Oct 21 2017 pre.log
-rw-r--r-- 1 dpx app 1241 Oct 21 2017 relocate2.sh
-bash-4.1$
-bash-4.1$ find /app/home/data01/dpx/ -type d -mtime +2 -name 'stress' -exec rm -rf {} \;
find: `/app/home/data01/dpx/stress': No such file or directory
-bash-4.1$ find /app/home/data01/dpx/ -type d -mtime +2 -name 'stress' -exec ls -ltr {} \;
Why am I getting this error and how I can prevent this?
find: `/app/home/data01/dpx/stress': No such file or directory

Resources