Bash ls and command pipe - bash

I'm trying to use the /etc/passwd file to list home directories of users in the system, sorted and without repetitions, such that nonexisting directories would not be printed..
This is my command:
cut -f 6 -d ':' /etc/passwd | sort -su | ls -ld
It acts as if I just ran ls -ld with no arguments from the command pipe at all.

You can't pipe stuff into ls.. You could do something like:
ls -ld $(cut -f 6 -d ':' /etc/passwd | sort -su)
By spawning a new bash to execute the cut | sort and passing it as a ls argument

ls does not take piped output. You could, however, use forward quotes to execute it on a list of directories:
ls `cut -f 6 -d ':' /etc/passwd | sort -su `

You could use xargs
cut -f 6 -d ':' /etc/passwd | sort -su | xargs ls

You were not far away, it was enough to add a xargs before ls :
cut -f 6 -d ':' /etc/passwd | sort -u | xargs ls -ld

Related

Count the number of files that modified in specif range of time [duplicate]

This question already has an answer here:
Getting all files which have changed in specific time range
(1 answer)
Closed 3 years ago.
I'm trying to build a one-line command in bash which counts the number of files of type *.lu in the current directory that were modified between 15:17 and 15:47 (date does not matter here). I'm not allowed to use find (otherwise it would been easy). I'm allowed to use basic commands like ls, grep, cut, wc and so on.
What I tried to do:
ls -la *.lu | grep <MISSING> | wc -l
First of all, I'll find all files *.lu, than I need to check with grep the date (which I'm not sure how to do) and than we need to count the number of lines. I think we need to insert also cut to get to the date and check it, but how? Also if current directory does not have *.lu files it will fail rather than returning 0.
How to solve it?
ls -l *.lu | grep -E '15:[2-3][0-9]|15:1[7-9]|15:4[0-7]' | wc -l
Should do it.
With awk:
ls -al *.lu | awk 'BEGIN{count=0} {if((substr($8,0,2) == "15") && (int(substr($8,4)) >=17 && int(substr($8,4)) <= 47 )){count++}} END{print count}'
UPDATE:
Without -E
ls -l *.lu | grep '15:[2-3][0-9]\|15:1[7-9]\|15:4[0-7]' | wc -l
Redirect error in case of zeros files:
ls -l *.lu 2> /dev/null | grep '15:[2-3][0-9]\|15:1[7-9]\|15:4[0-7]' | wc -l
This is pretty ugly and can probably be done better. However, I wanted to challenge myself to do this without regexes (excepting the sed one). I don't guarantee it'll handle all of your use cases (directories might be an issue).
ls -l --time-style="+%H%M" *.lu 2>/dev/null |
sed '/total/d' |
tr -s ' ' |
cut -d ' ' -f6 |
xargs -r -n 1 -I ARG bash -c '(( 10#ARG >= 1517 && 10#ARG <= 1547)) && echo ARG' |
wc -l
There is probably a way to avoid parsing ls via stat --format=%Y.

Largest file in the system and move it in unix

I am new to bash and im struggling with it. I have an assignment which the question is
Try to find that top 5 larger files in the entire file system ordered by size and move the file to /tmp folder and rename the file with current datetime format
I tried with the following code
du -a /sample/ | sort -n -r | head -n 5
Im getting the list, but i cannot able to move..
Suggestions please
Looks like a simple case of xargs:
du -a /sample/ | sort -n -r | head -n 5 | xargs -I{} mv {} /tmp
xargs here simply reads lines from standard input and appends them as arguments to the command, mv in this case. Because the -I{} is specified, the {} string is replaced for the argument by xargs. So mv {} /tmp is executed as mv <the first file> /tmp and mv <the second file> /tmp and so on. You can ex. add -t option to xargs or ex. add echo to see what's happenning: xargs -I{} -t echo mv {} /tmp.
Instead of running 5 processes, we could add /tmp on the end of the stream and run only one mv command:
{ du -a /sample/ | sort -n -r | head -n 5; echo /tmp; } | xargs mv
or like:
du -a . | sort -n -r | head -n 5 | { tee; echo /tmp; } | xargs mv
Note that using du -a will most probably not work with filenames with special characters, spaces, tabs and newlines. It will also include directories in it's output. If you want to filter the files only, move to much safer find:
find /sample/ -type f -printf '%s\t%p\n' | sort -n -r | cut -f2- | head -n5 | xargs -I{} mv {} /tmp
First we print each filename with it's size in bytes. Then we numerically sort the stream. Then we remove the size, ie. cut the stream on first '\t' tabulation. Then we get the head -n5 lines. Lastly, we copy with xargs. It will work for filenames not having special characters in filenames, like unreadable bytes, spaces, newlines and tabs.
For such corner cases it's preferred to use find and handle zero terminated strings, like this (note simply just -z and -0 options added):
find /sample/ -type f -printf '%s\t%p\0' | sort -z -n -r | cut -z -f2- | head -z -n5 | xargs -0 -I{} mv {} /tmp

run ls on the pipe output in shell script

I have to do below operation in one script.
/etc/passwd file to display user home directory ownership:
ls -lLd /<usershomedirectory>
I can grep the home path as following, please help me with running the ls on the home path based on the user home path that is identified using single line script.
grep "" /etc/passwd | cut -f 6 -d :
Updated
How to do it for some more ls
# ls -al /<usershomedirectory>/.login
# ls -al /<usershomedirectory>/.cschrc
# ls -al /<usershomedirectory>/.logout
# ls -al /<usershomedirectory>/.profile
# ls -al /<usershomedirectory>/.bash_profile
# ls -al /<usershomedirectory>/.bashrc
# ls -al /<usershomedirectory>/.bash_logout
# ls -al /<usershomedirectory>/.env
# ls -al /<usershomedirectory>/.dtprofile
# ls -al /<usershomedirectory>/.dispatch
# ls -al /<usershomedirectory>/.emacs
# ls -al /<usershomedirectory>/.exrc
You can use backticks/command output substitution:
ls -lLd "$(grep "^$username:" /etc/passwd | cut -f6 -d: )"
or piping to xargs:
grep "^$username:" /etc/passwd | cut -f 6 -d: | xargs -r ls -lLd
(I also added some more grep context to make sure the user matches exactly and supports blanks in file names)
The ~ expansion could be used with eval, not sure if thats a good idea:
eval "ls -lLd ~$username"
As #EtanReisner commented, the usual way to do this would be:
ls -lLd ~user
However, if you must use parsing of /etc/passwd and substitution, I would recommend this particular recipe:
ls -lLd $(awk -v acct="user" -F: '$1 == acct { print $NF-1 }' /etc/passwd)
But there's quite a few ways, as the other answer(s) will demonstrate as well...

Writing a shell script to obtain entries that contain a certain amount of integers?

I need help doing this I tried:
#!/bin/bash
ls -l [0-9][0-9]*
I am trying to get all the entries with 2 integers in them.
says ls cannot access no such file or directory
ls -l | awk '{print $9}' | grep '[0-9][0-9]'
Or if you simply want to count them:
ls -l | awk '{print $9}' | grep '[0-9][0-9]' | wc -l

Don't call xargs if the previous command doesn't return anything [duplicate]

This question already has answers here:
How to ignore xargs commands if stdin input is empty?
(7 answers)
Closed 6 years ago.
I have the following command that checks if any new files are added and automatically calls svn add on all these files
svn status | grep -v "^.[ \t]*\..*" | grep "^?" | awk '{print $2}' | xargs svn add
But when there are no files, svn add results in a warning.
How to stop from xargs from getting called the previous command doesn't result in any values? The solution needs to work with both GNU and BSD (Mac OS X) versions of xargs.
If you're running the GNU version, use xargs -r:
--no-run-if-empty
-r
If the standard input does not contain any nonblanks, do not run the command.
Normally, the command is run once even if there is no input. This option
is a GNU extension.
http://linux.die.net/man/1/xargs
If you're using bash, another way is to just store outputs in arrays. And run svn only if the there is an output.
readarray -t OUTPUT < <(exec svn status | grep -v "^.[ \t]*\..*" | grep "^?" | awk '{print $2}')
[[ ${#OUTPUT[#]} -gt 0 ]] && svn add "${OUTPUT[#]}"
I ended up using this. Not very elegant but works.
svn status | grep -v "^.[ \t]*\..*" | grep "^?" && svn status | grep -v "^.[ \t]*\..*" | grep "^?" | awk '{print $2}' | xargs svn add
ls /empty_dir/ | xargs -n10 chown root # chown executed every 10 args
ls /empty_dir/ | xargs -L10 chown root # chown executed every 10 lines
ls /empty_dir/ | xargs -i cp {} {}.bak # every {} is replaced with the args from one input line
ls /empty_dir/ | xargs -I ARG cp ARG ARG.bak # like -i, with a user-specified placeholder
https://stackoverflow.com/a/19038748/1655942

Resources