prevent globbing of argument to bash script - find

I want to invoke the "find" command inside myscript, using the arguments that I pass to myscript. But if I use wildcard characters with the "-name" argument and any file happens to match the string, the string is expanded to all the filenames, no matter what kind of "quotes" I 'use'.
In the shell, I want globbing, but not when I pass arguments to this script. Example:
~/u/tmp/JNK> ls -latTr
total 32
drwxr-xr-x 160 BNW staff 5440 Jan 27 18:47:37 2018 ../
-rw-r--r-- 1 BNW staff 0 Jan 27 19:30:10 2018 Henry-James.txt
-rw-r--r-- 1 BNW staff 0 Jan 27 19:30:17 2018 Emily-Dickinson.txt
-rw-r--r-- 1 BNW staff 0 Jan 27 19:30:21 2018 for-Henry-James.txt
-rwxr-xr-x 1 BNW staff 97 Jan 27 19:31:55 2018 myscript*
-rw-r--r--# 1 BNW staff 6148 Jan 27 19:43:15 2018 .DS_Store
drwxr-xr-x 2 BNW staff 68 Jan 27 19:44:51 2018 DIRECTORY/
lrwxr-xr-x 1 BNW staff 19 Jan 27 19:45:09 2018 softlink# -> for-Henry-James.txt
drwxr-xr-x 9 BNW staff 306 Jan 27 19:45:09 2018 ./
~/u/tmp/JNK> find . -type f
./.DS_Store
./Emily-Dickinson.txt
./for-Henry-James.txt
./Henry-James.txt
./myscript
~/u/tmp/JNK> find . -type f -name "*James*"
./for-Henry-James.txt
./Henry-James.txt
~/u/tmp/JNK> cat myscript
#!/bin/bash
for arg in $#
do
printf '%s' "arg="
printf '%s' "$arg"
printf '\n'
done
find . $#
~/u/tmp/JNK> ./myscript -type f
arg=-type
arg=f
./.DS_Store
./Emily-Dickinson.txt
./for-Henry-James.txt
./Henry-James.txt
./myscript
~/u/tmp/JNK> ./myscript -type f -name "*James*"
arg=-type
arg=f
arg=-name
arg=Henry-James.txt
arg=for-Henry-James.txt
find: for-Henry-James.txt: unknown primary or operator
~/u/tmp/JNK> ./myscript -type f -name '*James*'
arg=-type
arg=f
arg=-name
arg=Henry-James.txt
arg=for-Henry-James.txt
find: for-Henry-James.txt: unknown primary or operator
~/u/tmp/JNK>
Is there an elegant solution?

The solution appears to be to add one line
set -o noglob
to the script. Now we have
#!/bin/bash
set -o noglob
for arg in $#
do
printf '%s' "arg="
printf '%s' "$arg"
printf '\n'
done
find . $#
and
~/u/tmp/JNK> ./myscript -name "*Jam*"
arg=-name
arg=*Jam*
./for-Henry-James.txt
./Henry-James.txt
~/u/tmp/JNK>

Related

looking for shell / perl script to capture the folder time stamp and the files inside the folder

I am looking for script to capture the folder time stamp and the files inside the folder
Example: I have a folder Cform12 with files inside say note1.txt , note2.rtf , note3.ldt
ls -lrt will generate drwxr-xr-x 5 r12applprd dba 4096 Dec 4 02:31 Cform12
and
ls -lrt SCF6761-PROD will generate
total 12
-rwxr-xr-x 3 r12applprd dba 4096 Dec 4 02:30 note1.txt
-rwxr-xr-x 3 r12applprd dba 4096 Dec 4 02:30 note2.rtf
-rwxr-xr-x 26 r12applprd dba 4096 Dec 4 02:31 note3.ldt
Now i have output as
Dec 4 02:31 , Cform12 , note1.txt
Dec 4 02:31 , Cform12 , note2.txt
Dec 4 02:31 , Cform12 , note3.txt
Need help me with the shell or perl script for the same.
I have created the following script to help with my question .Thanks to the contributors for all the directions
use strict;
use warnings;
my $dir = '/tmp/';
opendir(DIR, $dir) or die $!;
while (my $file = readdir(DIR)) {
next unless (-d "$dir/$file");
my $fdir = "$dir/$file";
print "$fdir\n";
my $mtime = (stat $fdir)[9];
$mtime = localtime($mtime);
$mtime = echo "$mtime" | awk '{print \$2 , \$3, \$4}' 2 > /dev/ null;
chomp($mtime);
my #files1 = find $fdir -type f | awk -F "/" '{ print \$NF }';
foreach my $fil (#files1) { print "$mtime,$file,$fil"; }
}
closedir(DIR);
exit 0;
I think this script will help you if my understanding is correct..
use strict;
use warnings;
my $path="/var/inner";#directory absolute path
my $mtime=(stat $path)[9];
my $name=`basename $path`;
chomp($name);
$mtime= localtime($mtime);
$mtime=`echo "$mtime" | awk '{print \$2 , \$3, \$4}'`;
chomp($mtime);
my #files = glob("$path/*");
foreach my $file(#files){
print "$mtime , $name , ".`basename $file`;
}
The script below does the same but recursively. Is this what you want?
use strict;
use warnings;
my $path="/export/home/tarumugam/shellshop";#directory absolute path
sub rotator{
(my $path)=#_;
my $mtime=(stat $path)[9];
my $name=`basename $path`;
chomp($name);
$mtime= localtime($mtime);
$mtime=`echo "$mtime" | awk '{print \$2 , \$3, \$4}' 2> /dev/null`;
chomp($mtime);
my #files = glob("$path/*");
foreach my $file(#files){
if(-f $file){
print "$mtime , $name , ".`basename $file`;
}else{
rotator($file);
}
}
}
rotator($path);
Are you looking for something like the following?
$ stat -c "%x" src; find src -mindepth 1 -maxdepth 1 -exec basename '{}' \;
2013-12-03 22:39:42.911796567 -0500
UndirectedGraphClient.java
UndirectedGraph.java
Bag.java
A bash scripting guide could help you out a lot. Maybe take a look at http://mywiki.wooledge.org/BashGuide or http://wiki.bash-hackers.org/doku.php ?

awk, sed: one liner command for removing spaces from _all_ file names in a given folder?

Before:
eng-vshakya:scripts vshakya$ ls
American Samoa.png Faroe Islands.png Saint Barthelemy.png
After:
eng-vshakya:scripts vshakya$ ls
AmericanSamoa.png FaroeIslands.png SaintBarthelemy.png
Tried below prototype, but it does not work :( Sorry, not very good when it comes to awk/sed :(
ls *.png | sed 's/\ /\\\ /g' | awk '{print("mv "$1" "$1)}'
[ Above is prototype, real command, I guess, would be:
ls *.png | sed 's/\ /\\\ /g' | awk '{print("mv "$1" "$1)}' | sed 's/\ //g'
]
No need to use awk or sed when you can do this in pure bash.
[ghoti#pc ~/tmp1]$ ls -l
total 2
-rw-r--r-- 1 ghoti wheel 0 Aug 1 01:19 American Samoa.png
-rw-r--r-- 1 ghoti wheel 0 Aug 1 01:19 Faroe Islands.png
-rw-r--r-- 1 ghoti wheel 0 Aug 1 01:19 Saint Barthelemy.png
[ghoti#pc ~/tmp1]$ for name in *\ *; do mv -v "$name" "${name// /}"; done
American Samoa.png -> AmericanSamoa.png
Faroe Islands.png -> FaroeIslands.png
Saint Barthelemy.png -> SaintBarthelemy.png
[ghoti#pc ~/tmp1]$
Note that the ${foo/ /} notation is bash, and does not work in classic Bourne shell.
ghoti's solution is the right thing to do. Since you ask how to do it in sed, here's one way:
for file in *; do newfile=$( echo "$file" | tr -d \\n | sed 's/ //g' );
test "$file" != "$newfile" && mv "$file" "$newfile"; done
The tr is there to remove newlines in the filename, and is necessary to ensure that sed sees the entire filename in one line.

How to read a file and save the contents till we encounter the first blank line in perl script

I am trying to read a file and save the lines which starts with $path till it encounters first balnk line in an array. I have the below code, bt it only prints the path name and not the lines. Could some-one have a look.
Below are the contents of the $file:
\sbd\archieve\date\form
-rwxrwxrwx 1 etd maadm 4354270 Aug 16 21:56 COMAHCUT.dat.20120816.ftpd.201208162156*
-rw-r--r-- 1 etd maadm 0 Aug 16 21:56 COMAHCUT.DONE.20120816.ftpd.201208162156
\sbd\single\archieve\date\form
-rwxr-xr-x 1 etd maadm 1362780 Aug 15 22:02 COMAINS.dat.ftpd.201208152203*
-rwxr-xr-x 1 etd maadm 0 Aug 15 22:02 COMAINS.DONE.ftpd.201208152203*
Below is the code i tried:
#!/usr/bin/perl
my $file = "/home/pauler/practice/DataIt/line.txt";
open (INFO, $file) or die "Cannot open the file $file :$! \n";
my $path = "\sbd\archieve\date\form";
foreach $line (<INFO>) {
if ($line =~ m/$path/) {
push (#array1, $line);
last if ($line =~ m/^$/);
print #array1;
}
}
You can take advantage of the fact, that filehandles remember their position in the file.
use strict;
use warnings;
my #array;
my $path = '\sbd\archieve\date\form';
while ( my $line = <DATA> ) {
next unless $line =~ /\Q$path\E/;
push #array, $line;
while ( my $line = <DATA> ) {
last if $line =~ /^\s*$/;
push #array, $line;
}
}
print #array;
__DATA__
\sbd\archieve\date\form
-rwxrwxrwx 1 etd maadm 4354270 Aug 16 21:56 COMAHCUT.dat.20120816.ftpd.201208162156*
-rw-r--r-- 1 etd maadm 0 Aug 16 21:56 COMAHCUT.DONE.20120816.ftpd.201208162156
\sbd\single\archieve\date\form
-rwxr-xr-x 1 etd maadm 1362780 Aug 15 22:02 COMAINS.dat.ftpd.201208152203*
-rwxr-xr-x 1 etd maadm 0 Aug 15 22:02 COMAINS.DONE.ftpd.201208152203*
The flip-flop operator .. saves life ... our you code. It stays false until the expression on the left returns true, and remains true until the expression on the right turns true ... then it is false again until the left expressions evaluates to true again.
# read lines into $_ for cleaner code
while (<INFO>) {
if (/$path/ .. /^$/) {
push #array1, $_;
}
}
print #array1;
Oh, and a note on paths ... I know no single Operating System that really needs backslashes, not even Windows … Using normal slashes / will save you from weird escape sequences and other magic that lurks in the dark

Formatting localtime() in perl script

Wondering how to format the output of localtime() to year/month/day
I was able to do it easily using the 'date' command from terminal but I need to calculate previous dates as well, which I've figured out how to do in perl.
foreach my $i (0..7)
{
my $date = localtime(time() - 60*60*24*$i);
print "$i day(s) ago: $date\n";
}
Prints out this :
0 day(s) ago: Tue Apr 3 12:01:13 2012
1 day(s) ago: Mon Apr 2 12:01:13 2012
2 day(s) ago: Sun Apr 1 12:01:13 2012
3 day(s) ago: Sat Mar 31 12:01:13 2012
4 day(s) ago: Fri Mar 30 12:01:13 2012
5 day(s) ago: Thu Mar 29 12:01:13 2012
6 day(s) ago: Wed Mar 28 12:01:13 2012
7 day(s) ago: Tue Mar 27 12:01:13 2012
Here's an example of POSIX::strftime:
use POSIX ();
my #local = ( localtime )[0..5];
foreach my $i ( 0..7 ) {
my $date = POSIX::strftime( '%a %b %d %H:%M:%S %Y', #local);
print "$i day(s) ago: $date\n";
$local[3]--;
}
If you are doing date math, use a module that does it right. For instance, DateTime:
use DateTime;
my $date = DateTime->now;
foreach my $i ( 0 .. 10 ) {
$date->subtract( days => 1 );
say $date->ymd( '/' );
}
you could use POSIX::strftime from POSIX module.
perl -mPOSIX -e 'printf POSIX::strftime("%Y/%m/%d",localtime).
Script:
#!/usr/bin/perl
use strict;
foreach my $i (0..7)
{
my ($d, $m, $y) = (localtime(time() - 60*60*24*$i))[3,4,5];
printf "%d day(s) ago: %d/%d/%d\n", $i, $y+1900, $m+1, $d;
}
Output:
0 day(s) ago: 2012/4/3
1 day(s) ago: 2012/4/2
2 day(s) ago: 2012/4/1
3 day(s) ago: 2012/3/31
4 day(s) ago: 2012/3/30
5 day(s) ago: 2012/3/29
6 day(s) ago: 2012/3/28
7 day(s) ago: 2012/3/27

date conversion DD-MMM-YY to YYYYMMDD

How to convert DD-MMM-YY to YYYYMMDD
I am in AIX, using korn shell.
neither date --date nor date -d works in aix.
pure ksh:
convert_date () {
typeset -l date=$1
typeset IFS="-"
set -- $date # now $1 is the day, $2 is the lower-case month, $3 is the year
typeset months
set -A months "" jan feb mar apr may jun jul aug sep oct nov dec
typeset -i m=1
while [[ $m -le 12 ]]; do
if [[ "$2" = "${months[$m]}" ]]; then
break
else
m=$(( m+1 ))
fi
done
# assume this century
printf "20%02d%02d%02d\n" "$3" $m "$1"
}
convert_date 06-JUL-11 # ==> 20110706
If you have tclsh on your system:
old="06-JUL-11"
new=$( echo "puts [clock format [clock scan $old] -format %Y%m%d]" | tclsh )
echo $new # ==> 20110706
I did it in perl, the hard way
sub formatDate_YYYYMMDD
{
my $date=shift;
my ($day,$mon,$yr) = split /\-/,$date;
my #months=qw(JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC );
$i=0;
foreach $_ (#months)
{
$i++;
if (/$mon/i){$mon=$i;}
}
$year=2000+$yr;
$mon=sprintf "%02d",$mon;
print "Invalid month format $date present in $file_name" if $mon ==0;
return("$year$mon$day");
}