store a result of a command into a variable using csh - unix

I'm trying to store a result of a command into a variable so I can display it nicely along with some text in one long not have it display the output and then newline then my text, in my csh script.
#! /bin/csh -f
if ("$1" == "-f" && $#argv == 1 ) then
grep 'su root' /var/adm/messages.[0-9] | cut -c 21-250
grep 'su root' /var/adm/messages
else if( $#argv > 0 ) then
echo "Usage : [-f]"
else
grep 'su root' /var/adm/messages.[0-9] /var/adm/messages | wc -l
printf "failed su attempts between Nov 02 and Oct 28\n"
endif
this command in the script
grep 'su root' /var/adm/messages.[0-9] /var/adm/messages | wc -l
gives me 21 when i run it, and i want 21 to be stored in a variable.
so i can just display the output of
21 failed su attempts between Nov 02 and Oct 28
and not
21
failed su attempts between Nov 02 and Oct 28
or if theres an easier way that doesn't involve variables open to that too.

You can use set and backticks (``). Something like
set count=`grep 'su root' /var/adm/messages.[0-9] /var/adm/messages | wc -l`
printf "$count failed su attempts between Nov 02 and Oct 28\n"
or
printf "%s failed su attempts between Nov 02 and Oct 28\n" "$count"
or without a variable, like
printf "%s failed su attempts between Nov 02 and Oct 28\n" \
`grep 'su root' /var/adm/messages.[0-9] /var/adm/messages | wc -l`

Related

Executing Linux/Unix Command From Within R Using Variables

I'm trying to make a call from within R to execute BASH commands, to get my feet wet:
I wanted to simply capture a listing of my current files located in a specific directory through use of the "ls -al" command. The output would be sent to text file called a01_test.txt.
The directory I would like to capture the contents of is "C:\Users\user00\a01_TEST" which is referenced as "/mnt/c/Users/user00/a01_TEST/" from a WSL Ubuntu 20.04.5 LTS perspective.
The directory contains five (5) files: file_01.txt, file_02.txt ,..., file_05.txt.
FYI, I am running R (R version 4.2.0 (2022-04-22 ucrt)) via RStudio (2022.07.1 Build 554) on Windows 11 (Version 10.0.22000 Build 22000).
I tried:
PATH_UNIX <- "/mnt/c/Users/user00/a01_TEST/"
FILENAME_TEST <-"a01_test.txt"
paste0("system(\"bash -c \'ls -al ",PATH_UNIX," >",PATH_UNIX,FILENAME_TEST,"\'\")")
However that only returned a command prompt -- nothing else:
> paste0("system(\"bash -c \'ls -al ",PATH_UNIX," >",PATH_UNIX,FILENAME_TEST,"\'\")")
[1] "system(\"bash -c 'ls -al /mnt/c/Users/user00/a01_TEST/ >/mnt/c/Users/user00/a01_TEST/a01_test.txt'\")"
>
I thought one could test the code using:
cat(print(paste0("system(\"bash -c \'ls -al ",PATH_UNIX," >",PATH_UNIX,FILENAME_TEST,"\'\")")))
which resulted in:
> cat(print(paste0("system(\"bash -c \'ls -al ",PATH_UNIX," >",PATH_UNIX,FILENAME_TEST,"\'\")")))
[1] "system(\"bash -c 'ls -al /mnt/c/Users/user00/a01_TEST/ >/mnt/c/Users/user00/a01_TEST/a01_test.txt'\")"
system("bash -c 'ls -al /mnt/c/Users/user00/a01_TEST/ >/mnt/c/Users/user00/a01_TEST/a01_test.txt'")
If I do not use variables, such as, PATH_UNIX and FILENAME_TEST and code the entire path manually, I can create a text file (a01_test.txt) giving me the desired listing of the directory's contents:
system("bash -c 'ls -al /mnt/c/Users/user00/a01_TEST > /mnt/c/Users/user00/a01_TEST/a01_test.txt'")
which results in:
> system("bash -c 'ls -al /mnt/c/Users/user00/a01_TEST > /mnt/c/Users/user00/a01_TEST/a01_test.txt'")
[1] 0
>
giving me the file called "a01_test.txt" containing the directory's contents:
total 0
drwxrwxrwx 1 user00 user00 4096 Nov 3 2022 .
drwxrwxrwx 1 user00 user00 4096 Nov 3 05:07 ..
-rwxrwxrwx 1 user00 user00 0 Nov 3 2022 a01_test.txt
-rwxrwxrwx 1 user00 user00 0 Nov 3 05:26 file_01.txt
-rwxrwxrwx 1 user00 user00 0 Nov 3 05:26 file_02.txt
-rwxrwxrwx 1 user00 user00 0 Nov 3 05:26 file_03.txt
-rwxrwxrwx 1 user00 user00 0 Nov 3 05:26 file_04.txt
-rwxrwxrwx 1 user00 user00 0 Nov 3 05:26 file_05.txt
Any assistance to make use of the variables PATH_UNIX & FILENAME_TEST to make a call to Linux/Unix to obtain a directory listing would be appreciated.
sprintf (?sprintf for further details) is a convenient way to create format strings that can subsequently be passed to system:
PATH_UNIX <- '/mnt/c/Users/user00/a01_TEST/'
FILENAME_TEST <- 'a01_test.txt'
cmdstr <- sprintf('bash -c \'ls -al %s > %s\'', PATH_UNIX, FILENAME_TEST)
message('bash command string = ', cmdstr)
system(command = cmdstr)
Expanding on the solution provided by br00t, and doing some testing, one could also use the paste0() function:
# DESIRED CMD TO BE PASSED VIA BASH
cat(paste0("system(bash -c \'ls -al ",PATH_UNIX," >",PATH_UNIX,FILENAME_TEST,"\')"))
# OUTPUT:
# system(bash -c 'ls -al /mnt/c/Users/user00/a01_TEST/ >/mnt/c/Users/user00/a01_TEST/a01_test.txt')
# PLACE DESIRED CMD IN A VAR:
cmdstr_test <- paste0("bash -c \'ls -al ",PATH_UNIX," > ",PATH_UNIX,FILENAME_TEST,"\'")
# CHECK VAR:
message('bash command string = ', cmdstr_test)
# OUTPUT:
# bash command string = bash -c 'ls -al /mnt/c/Users/user00/a01_TEST/ > /mnt/c/Users/user00/a01_TEST/a01_test.txt'
# RUN COMMAND USING system() function:
system(command = cmdstr_test)
# OUTPUT (Will get "0", if successful)
> system(command = cmdstr_test)
[1] 0
>

Drop 4 first columns

I have a command that can drop first 4 columns, but unfortunately if 2nd column name and 4th column name likely similar, it will truncate at 2nd column but if 2nd column and 4th column name are not same it will truncate at 4th column. Is it anything wrong to my commands?
**
awk -F"|" 'NR==1 {h=substr($0, index($0,$5)); next}
{file= path ""$1""$2"_"$3"_"$4"_03042017.csv"; print (a[file]++?"": "DETAILS 03042017" ORS h ORS) substr($0, index($0,$5)) > file}
END{for(file in a) print "EOF " a[file] > file}' filename
**
Input:
Account Num | Name | Card_Holder_Premium | Card_Holder| Type_Card | Balance | Date_Register
01 | 02 | 03 | 04 | 05 | 06 | 07
Output
_Premium | Card_Holder| Type_Card | Balance | Date_Register
04 | 05 | 06 | 07
My desired output:
Card_Holder| Type_Card | Balance | Date_Register
05 | 06 |07
Is this all you're trying to do?
$ sed -E 's/([^|]+\| ){4}//' file
April | May | June
05 | 06 | 07
$ awk '{sub(/([^|]+\| ){4}/,"")}1' file
April | May | June
05 | 06 | 07
The method you use to remove columns using index is not correct. As you have figured out, index can be confused and match the previous field when the previous field contains the same words as the next field.
The correct way is the one advised by Ed Morton.
In this online test, bellow code based on Ed Morton suggestion, gives you the output you expect:
awk -F"|" 'NR==1 {sub(/([^|]+\|){3}/,"");h=$0;next} \
{file=$1$2"_"$3"_"$4"_03042017.csv"; sub(/([^|]+\|){3}/,""); \
print (a[file]++?"": "DETAILS 03042017" ORS h ORS) $0 > file} \
END{for(file in a) print "EOF " a[file] > file}' file1.csv
#Output
DETAILS 03042017
Card_Holder| Type_Card | Balance | Date_Register
04 | 05 | 06 | 07
EOF 1
Due to the whitespace that you have include in your fields, the filename of the generated file appears as 01 02 _ 03 _ 04 _03042017.csv. With your real data this filename should appear correct.
In any case, i just adapt Ed Morton answer to your code. If you are happy with this solution you should accept Ed Morton answer.
PS: I just removed a space from Ed Morton answer since it seems to work a bit better with your not so clear data.
Ed Suggested:
awk '{sub(/([^|]+\| ){4}/,"")}1' file
#Mind this space ^
This space here it might fail to catch your data if there is no space after each field (i.e April|May).
On the other hand, by removing this space it seems that Ed Solution can correctly match either fields in format April | May or in format April|May

Trying to fetch the unix logs for a given timeframe [duplicate]

This question already has answers here:
Filter log file entries based on date range
(5 answers)
Closed 6 years ago.
I am struggling to get the logs from a file for a given timeframe.
I have gone through other posts but everywhere the date/time has been hardcoded, I don't want it to be hardcoded and would like to fetch it programatically.
This is what I have been trying.
This works,
awk -F, -v b='2016-08-10 00:40:06' -v e='date +"%F %T"' '{ if ($1 >= b && $1 <= e) print}' filename
Whereas this doesn't work,
awk -F, -v b='date --date="10 minutes ago" +"%F %T"' -v e='date +"%F %T"' '{ if ($1 >= b && $1 <= e) print}' filename
Not sure why the first Date parameter is not getting calculated on the fly?
Try something like this:
awk -v b="`date --date '10 minutes ago'`"
Example:
date;echo 1 2 3 |awk -v d="`date --date '10 minutes ago'`" '{print d}'
Wed Aug 10 01:57:23 EDT 2016
Wed Aug 10 01:47:23 EDT 2016
echo 1 2 3 | awk -v d="`date --date '10 minutes ago'`" -v e="`date +%F%T`" '{print d, e}'
Wed Aug 10 01:50:05 EDT 2016 2016-08-1002:00:05

Format output of concatenating 2 variables in unix

I am coding a simple shell script that checks the space of the target path and the space utilization per directory on that target path (example, I am checking space of /path1/home, and also checks how all the folders on /path1/home is consuming the total space.) My question is regarding the output it produces, it is not that pleasing to the eye (uneven spacing). See sample output lines below.
SIZE USER_FOLDER DATE_LAST_MODIFIED
83G FOLDER 1 Apr 15 03:45
34G FOLDER 10 Mar 9 05:02
26G FOLDER 11 Mar 29 13:01
8.2G FOLDER 100 Apr 1 09:42
1.8G FOLDER 101 Apr 11 13:50
1.3G FOLDER 110 Feb 16 09:30
I just want the output format to be in line with the header so it will look neat because I will use it as a report. Here is the code I am using for this part.
ls -1 | grep -v "lost+found" |grep -v "email_body.tmp" > $v_path/Users.tmp
for user in `cat $v_path/Users.tmp | grep -v "Users.tmp"`
do
folder_size=`du -sh $user 2>/dev/null` # should be run using a more privileged user so that other folders can be read (2>/dev/null was used to discard error messages i.e. "du: cannot read directory `./marcnad/.gnupg': Permission denied")
folder_date=`ls -ltr | tr -s " " | cut -f6,7,8,9, -d" " | grep -w $user | cut -f1,2,3, -d" "`
folder_size="$folder_size $folder_date"
echo $folder_size >> $v_path/Users_Usage.tmp
done
echo "Summary of $v_path Disk Space Utilization per folder." >> email_body.tmp
echo "" >> email_body.tmp
echo "SIZE USER_FOLDER DATE_LAST_MODIFIED" >> email_body.tmp
for i in T G M K
do
cat $v_path/Users_Usage.tmp | grep [0-9]$i | sort -nr -k 1 >> $v_path/email_body.tmp
done
Thanks!
EDIT: Formatting
When you print the data use printf instead of echo
cat $v_path/Users_Usage.tmp | while read a b c d e f
do
printf '%-5s%-7%s%-4s%-4s%-3s-6s' $a $b $c $d $e $f
done
See here

How to properly grep filenames only from ls -al

How do I tell grep to only print out lines if the "filename" matches when I'm piping through ls? I want it to ignore everything on each line until after the timestamp. There must be some easy way to do this on a single command.
As you can see, without it, if I searched for the file "rwx", it would return not only the line with rwx.c, but also the first three lines because of permissions. I was going to use AWK but I want it to display the whole last line if I search for "rwx".
Any ideas?
EDIT: Thanks for the hacks below. However, it would be great to have a more bug-free method. For example, if I had a file named "rob rob", I wouldn't be able to use the stated solutions.
drwxrwxr-x 2 rob rob 4096 2012-03-04 18:03 .
drwxrwxr-x 4 rob rob 4096 2012-03-04 12:38 ..
-rwxrwxr-x 1 rob rob 13783 2012-03-04 18:03 a.out
-rw-rw-r-- 1 rob rob 4294 2012-03-04 18:02 function1.c
-rw-rw-r-- 1 rob rob 273 2012-03-04 12:54 function1.c~
-rw-rw-r-- 1 rob rob 16 2012-03-04 18:02 rwx.c
-rw-rw-r-- 1 rob rob 16 2012-03-04 18:02 rob rob
The following will list only file name, and one file in each row.
$ ls -1
To include . files
$ ls -1a
Please note that the argument is number "1", not letter "l".
Why don't you use grep and match the file name following the timestamp?
grep -P "[0-9]{2}:[0-9]{2} $FILENAME(\.[a-zA-Z0-9]+)?$"
The [0-9]{2}:[0-9]{2} is for the time, the $FILENAME is where you'd put rob rob or rwx, and the trailing (\.[a-zA-Z0-9]+)? is to allow for an optional extension.
Edit: #JonathanLeffler below points out that when files are older than bout 6 months the time column gets replaced by a year - this is what happens on my computer anyhow. You could do ([0-9]{2}:[0-9]{2}|(19|20)[0-9]{2}) to allow time OR year, but you may be best of using awk (?).
[foo#bar ~/tmp]$ls -al
total 8
drwxrwxr-x 2 foo foo 4096 Mar 5 09:30 .
drwxr-xr-- 83 foo foo 4096 Mar 5 09:30 ..
-rw-rw-r-- 1 foo foo 0 Mar 5 09:30 foo foo
-rw-rw-r-- 1 foo foo 0 Mar 5 09:29 rwx.c
-rw-rw-r-- 1 foo foo 0 Mar 5 09:29 tmp
[foo#bar ~/tmp]$export filename='foo foo'
[foo#bar ~/tmp]$echo $filename
foo foo
[foo#bar ~/tmp]$ls -al | grep -P "[0-9]{2}:[0-9]{2} $filename(\.[a-zA-Z0-9]+)?$"
-rw-rw-r-- 1 cha66i cha66i 0 Mar 5 09:30 foo foo
(You could additionally extend to matching the whole line if you wanted:
^ # start of line
[d-]([r-][w-][x-]){3} + # permissions & space (note: is there a 't' or 's'
# sometimes where the 'd' can be??)
[0-9]+ # whatever that number is
[\w-]+ [\w-]+ + # user/group (are spaces allowed in these?)
[0-9]+ + # file size (modify for -h switch??)
(19|20)[0-9]{2}- # yyyy (modify if you want to allow <1900)
(1[012]|0[1-9])- # mm
(0[1-9]|[12][0-9]|3[012]) + # dd
([01][0-9]|2[0-3]):[0-6][0-9] +# HH:MM (24hr)
$filename(\.[a-zA-Z0-9]+)? # filename & optional extension
$ # end of line
. You get the point, tailor to your needs.)
Assuming that you aren't prepared to do:
ls -ld $(ls -a | grep rwx)
then you need to exploit the fact that there are 8 columns with space separation before the file name starts. Using egrep (or grep -E), you could do:
ls -al | egrep "^([^ ]+ +){8}.*rwx"
This looks for 'rwx' after the 8th column. If you want the name to start with rwx, omit the .*. If you want the name to end with rwx, add a $ at the end. Note that I used double quotes so you could interpolate a variable in place of the literal rwx.
This was tested on Mac OS X 10.7.3; the ls -l command consistently gives three columns for the date field:
-r--r--r-- 1 jleffler staff 6510 Mar 17 2003 README,v
-r--r--r-- 1 jleffler staff 26676 Mar 3 21:44 ccs.nmd
Your ls -l seems to be giving just two columns, so you'd need to change the {8} to {7} for your machine - and beware migrating between systems.
Well, if you're working with filenames that don't have spaces in them, you could do something like this:
grep 'rwx\S*$'
Aside frrm the fact that you can use pattern matching with ls, exaple ksh and bash,
which is probably what you should do, you can use the fact that filename occur in a
fixed position. awk (gawk, nawk or whaever you have) is a better choice for this.
If you have to use grep it smells like homework to me. Please tag it that way.
Assume the filename starting position is based on this output from ls -l in linux: 56
-rwxr-xr-x 1 Administrators None 2052 Feb 28 20:29 vote2012.txt
ls -l | awk ' substr($0,56) ~/your pattern even with spaces goes here/'
e.g.,
ls -l | awk ' substr($0,56) ~/^val/'
will find files starting with "val"
As a simple hack, just add a space before your filename so you don't match the beginning of the output:
ls -al | grep '\srwx'
Edit: OK, this is not as robust as it should be. Here's awk:
ls -l | awk ' $9 ~ /rwx/ { print $0 }'
This works for me, unlike ls -l & others as some folks pointed out. I like this because its really generic & gives me the base file name, which removes the path names before the file.
ls -1 /path_name |awk -F/ '{print $NF}'
Only one command you needed for this --
ls -al | gawk '{print $9}'
You can use this:
ls -p | grep -v /
this is super old, but i needed the answer and had a hard time finding it. i didn't really care about the one-liner part; i just needed it done. this is down and dirty and requires that you count the columns. i'm not looking for an upvote here, just leaving some options for future searcher-ers.
the helpful awk trick is here -- Using awk to print all columns from the nth to the last
if
YOUR_FILENAME="rob rob"
and
WHERE_FILENAMES_START=8
ls -al | while read x; do
y=$(echo "$x" | awk '{for(i=$WHERE_FILENAMES_START; i<=NF; ++i) printf $i""FS; print ""}')
[[ "$YOUR_FILENAME " = "$y" ]] && echo "$x"
done
if you save it as a bash script and swap out the vars with $2 and $1, throw the script in your usr bin... then you'll have your clean simple one-liner ;)
output will be:
> -rw-rw-r-- 1 rob rob 16 2012-03-04 18:02 rob rob
the question was for a one-liner so...
ls -al | while read x; do [[ "$YOUR_FILENAME " = "$(echo "$x" | awk '{for(i=WHERE_FILENAMES_START; i<=NF; ++i) printf $i""FS; print ""}')" ]] && echo "$x" ; done
(lol ;P)
on another note: mathematical.coffee your answer was rad. it didn't solve my version of this problem, so i didn't upvote, but i liked your regex breakdown :D

Resources