printf "2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n" | awk -F\| 'if ( -f $2 ) { print $2}'
or
printf "2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n" | awk -F\| '{if (system("test -f" $2)) print $2}'
/home/user/.ssh/config\n2015-03-02 - exists
/home/user/Desktop/temp328 - removed
I want print only exist files but this commands not working.
The second attempt was fairly close; you need a space after the test -f
.
base$ echo '2015|/etc/mtab
> 2015|/etc/ntab' | awk -F\| '{ if (system("test -f " $2)) print $2}'
/etc/ntab
You probably want to invert to use if (system(...)==0)
to get the semantics you expected. Also, somewhat more elegantly, Awk wants a condition outside the braces, so you can avoid the explicit if
.
awk -F\| 'system("test -f " $2)==0 { print $2 }'
Agree with commenters that using Awk for this is borderline nuts.
If, as indicated in comments, you need to work with completely arbitrary file names, you can add code to quote any shell specials:
awk -F\| 'system ("test -f " gensub(/[^\/A-Za-z0-9]/, "\\\\&", "g", $2))==0 {
print $2 }' # caveat: gensub() is gawk only
... but your overall solution does not cope with file names containing a newline character or a pipe character (since you are using those as record and field separators, respectively) so again, abandoning Awk and starting over with a different approach may be the sane way forward.
(The character class in the substitution is incomplete; there are various punctuation characters etc which could be added, and I may be missing something significant; but on quick examination, the superfluous backslashes should be harmless. If you don't have Gawk, see here and/or, again, consider abandoning this approach.)
while IFS='|' read -r stuff filename; do
test -f "$filename" && echo "$filename"
done <<':'
2015|/etc/mtab
2016|/etc/ntab
2017|/path/to/file with whitespace in name
2018|/path/to/file\with[funny"characters*in(file'name|even pipes, you see?
:
(Still no way to have a newline, but everything else should be fine.)
With GNU awk you can use stat()
included with the filefuncs
extension:
$ ls -l
-rw-r--r-- 1 james james 4 Oct 3 12:48 foo
-rw------- 1 root root 0 Oct 3 12:48 bar
Awk:
$ awk -v file=foo '
@load "filefuncs"
BEGIN {
ret=stat(file,fdata)
printf "ret: %d\nsize: %d\n",ret,fdata["size"]
}'
Output for -v file= foo
:
ret: 0
size: 4
for bar
:
ret: 0
size: 0
and for nonexistent baz
:
ret: -1
size: 0
It's easy to check for the existence of a readable file in awk, without having to resort to spawning something with system()
. Just try to read from the file.
From awk's man page (on my system anyway):
In all cases, getline returns 1 for a successful input, 0 for end of file, and -1 for an error.
So. Some example code.
#!/usr/bin/awk -f
function file_exists(file) {
n=(getline _ < file);
if (n > 0) {
print "Found: " file;
return 1;
} else if (n == 0) {
print "Empty: " file;
return 1;
} else {
print "Error: " file;
return 0;
}
}
BEGIN {
file_exists(ARGV[1]);
}
Gives me these results:
$ touch /tmp/empty
$ touch /tmp/noperm ; chmod 000 /tmp/noperm
$ ./check.awk /etc/passwd
Found: /etc/passwd
$ ./check.awk /nonexistent
Error: /nonexistent
$ ./check.awk /tmp/empty
Empty: /tmp/empty
$ ./check.awk /tmp/noperm
Error: /tmp/noperm
Using your sample data:
$ fmt="2015-03-02|/home/user/.ssh/config\n2015-03-02|/home/user/Desktop/temp328\n"
$ printf "$fmt" | cut -d\| -f2 | xargs -n 1 ./check.awk
Error: /home/user/.ssh/config
Error: /home/user/Desktop/temp328
For more general use, you could shorten this function to something like:
function file_exists(file) {
if ((getline _ < file) >= 0) { return 1; }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With