Checking if subfolders exist linux - linux

I'm trying to check if a folder has any subfolders without iterating through its children, in Linux. The closest I've found so far is using ftw and stopping at the first subfolder - or using scandir and filtering through the results. Both, are, however, an overkill for my purposes, I simply want a yes/no.
On Windows, this is done by calling SHGetFileInfo and then testing dwAttributes & SFGAO_HASSUBFOLDER on the returned structure. Is there such an option on Linux?

The standard answer is to call stat on the directory, then check the st_nlink field ("number of hard links"). On a standard filesystem, each directory is guaranteed to have 2 hard links (. and the link from the parent directory to the current directory), so each hard link beyond 2 indicates a subdirectory (specifically, the subdirectory's .. link to the current directory).
However, it's my understanding that filesystems aren't required to implement this (see, e.g., this mailing list posting), so it's not guaranteed to work.
Otherwise, you have to do as you're doing:
Iterate over the directory's contents using glob with the GNU-specific GLOB_ONLYDIR flag, or scandir, or readdir.
Call stat on each result and check S_ISDIR(s.st_mode) to verify that files found are directories. Or, nonportably, check struct dirent.d_type: if it's DT_DIR then it's a file, and if it's DT_UNKNOWN, you'll have to stat it after all.

The possibilities you've mentioned (as well as e.James's) seem to me like they're better suited to a shell script than a C++ program. Presuming the "C++" tag was intentional, I think you'd probably be better off using the POSIX API directly:
// warning: untested code.
bool has_subdir(char const *dir) {
std::string dot("."), dotdot("..");
bool found_subdir = false;
DIR *directory;
if (NULL == (directory = opendir(dir)))
return false;
struct dirent *entry;
while (!found_subdir && ((entry = readdir(directory)) != NULL)) {
if (entry->d_name != dot && entry->d_name != dotdot) {
struct stat status;
stat(entry->d_name, &status);
found_subdir = S_ISDIR(status.st_mode);
}
}
closedir(directory);
return found_subdir;
}

Does getdirentries do want you want it to do? I think it shoudl return nothing if there are no directories. I would have tried this myself but am temporarily without access to a linux box :(

Related

Why can't Groovy find my file?

I wrote a script in groovy to find files java test files recursively in a given directory with certain names, the concerned part of the code is:
def projectRootDirectory = args.length ? new File(args[0]) : new File(System.getProperty("user.dir"))
def srcFilesCount = 0, testFilesCount = 0, srcLinesCount=0, testLinesCount=0
def srcFileSubstringPattern = '.java'
def testFileSubstringPattern = 'Test.java'
projectRootDirectory.eachDirRecurse() { dir ->
dir.eachFile {
if (it.name.endsWith(testFileSubstringPattern) || it.name ==~ /Test.*java/ ||
it.name.endsWith('Tests.java') || it.name.endsWith('TestCase.java')) {
//println "Test file found: " + it.name
testFilesCount++
it.eachLine { testLinesCount++ }
} else if (it.name.contains(srcFileSubstringPattern)) {
srcFilesCount++
it.eachLine { srcLinesCount++ }
}
}
}
It finds already existing files in the repo which was cloned using SVN that match for example someTestCase.java, but when I created some new ones by using the command touch dummyTestCase.java via Cygwin in Windows 7 or via the Windows 7 explorer right click -> New -> Text Document option and rename it to something like TestDummy.java, it doesn't find them. The script also treats copies of the respective files the same way i.e. it finds copies of old files that already existed but not the new ones I create. I even opened up file permissions to fullest on the newly created files, but no change. Whereas the BASH find command via Cygwin always finds all the files without any issue. I have confirmed using diagnostic print statements the the script is looking in the correct directory. I even confirmed this by having the script create some files there and confirmed they got created in the correct place.
Wow, the answer turned out to be amazingly simple. I replaced eachDirRecurse with eachFileRecurse thus also eliminating the nested loop. Thanks a ton to all the comment authors whose help led me to this discovery.

Why os.Open returns nil error when opening a directory?

I've spend some time looking for an error in my code and it appeared that at one place I was trying to read contents of a directory like it was the file. Consider the following code:
import (
"fmt"
"os"
)
func init() {
file, err := os.Open("/tmp/")
fmt.Println(file, err) //err == nil here
var b []byte
n, err := file.Read(b)
fmt.Println(n, err) //err == "read /tmp/: is a directory"
}
I am wondering, why os.Open allows to 'open' a directory without an error if I cannot 'read' it anyway? The documentation says
Open opens the named file for reading. If successful, methods on the
returned file can be used for reading; the associated file descriptor
has mode O_RDONLY. If there is an error, it will be of type
*PathError. [reference]
If the 'directory' is a 'file' is disputable but for me it looks a bit misleading. Is there any usage for that behavior?
"Reading" has more meanings, one of which is reading the contents of a file.
Another meaning is if the file denotes a directory, you may read its content, which is the list of the files / subfolders in it, using File.Readdir() or File.Readdirnames(). This is perfectly valid for an opened file whose name denotes a directory.
Also you may do a lot more with an opened os.File even if it denotes a folder, e.g. call its File.Chdir() (exclusive for directories) or File.Chmod() methods, or get statistics about it using File.Stat(). I don't see why "opening" a folder should be disallowed. The wording in the doc may not be perfect (or it could be extended to mention this though).

Check latest file updates in directory on linux using bash shell scripting

I have basic knowledge of linux bash shell scripting, right now I am facing a problem that is like following:
Suppose I am working in an empty directory mydir
Then there is a process which is created by a C program to generate a file with one word. (Exp: file.txt would have one word, "hello")
Routinely, after a specific period of time, the file is updated by the C program with the same one word "hello".
I want check the file every time when it is updated.
But the issue is that I also want my script doing some other operation while checking the file updates and when it detects file updates that it returns something for which I can use to trigger something else.
So, can anyone help me.
Also, some proof of concept :
while true;
do
func1();
func2();
check file is updated or not
if updated ; then
break;
else
continue;
You probably want the stat command. Do man stat to see how yours works. You want to look for "modtime" or "time of last data modification" option. For mine that would be stat -c%Y file. Something like basemodtime=$(stat -c%Y file) before the loop, modtime=$(stat -c%Y file) after func2(), and then if [ $modtime != $basemodtime ]; then to detect "updated".

file command generating 'invalid argument'

I have a perl script that traverses a set of directories and when it hits one of them it blows up with an Invalid Argument and I want to be able to programmatically skip it. I thought I could start by finding out the file type with the file command but it too blows up like this:
$ file /sys/devices/virtual/net/br-ex/speed
/sys/devices/virtual/net/br-ex/speed: ERROR: cannot read `/sys/devices/virtual/net/br-ex/speed' (Invalid argument)
If I print out the mode of the file with the perl or python stat function it tells me 33060 but I'm not sure what all the bits mean and I'm hoping a particular one would tell me not to try to look inside. Any suggestions?
To understand the stats number you got, you need to convert the number to octal (in python oct(...)).
Then you'll see that 33060 interprets to 100444. You're interested only in the last three digits (444). The first digit is file owner permissions, the second is group and the third is everyone else.
You can look at each of the numbers (in your case all are 4) as 3 binary bits in this order:
read-write-execute.
Since in your case owner, group & other has 4, it is translated (for all of them) to 100 (in binary) which means that only the read bit is on for all three - meaning that all three can only read the file.
As far as file permissions go, you should have been successful reading /sys/devices/virtual/net/br-ex/speed.
There are two reasons for the read to fail:
- Either speed is a directory, (directories require execute permissions to read inside).
- Or it's a special file - which can be tested using the -f flag in perl or bash, or using os.path.isfile(...) in python.
Anyhow, you can use the following links to filter files & directories according to their permissions in the 3 languages you mentioned:
ways to test permissions in perl.
ways to test permissions in python.
ways to test permissions in bash.
Not related to this particular case, but I hit the same error when I ran it on a malicious ELF (Linux executable) file. In that case it was because the program headers of the ELF was intentionally corrupted. Looking at the source code for file command, this is clear as it checks the ELF headers and bails out with the same error in case the headers are corrupted:
/*
* Loop through all the program headers.
*/
for ( ; num; num--) {
if (pread(fd, xph_addr, xph_sizeof, off) <
CAST(ssize_t, xph_sizeof)) {
file_badread(ms);
return -1;
}
TLDR; The file command checks not only the magic bytes, but it also performs other checks to validate a file type.

capturing command line output in ncurses

how to capture the command line output in a window using ncurses?
Suppose I am excecuting a command like "ls" I want to print that output in a specific window which is designed in ncurses. I am new to ncurses.help me.Thanks in advance
One thing I can think of is using system() to execute the command, redirecting its output to a temp file:
system("ls > temp");
Then opening the file temp, reading its content and displaying it on the window.
Not an elegant solution, but works.
The more elegant solution might be to implement the redirect within your program. Look into the dup() and dup2() system calls (see the dup(2) manpage). So, what you would want to do is (this is essentially what the shell called by system() ends up doing):
Code Snippet:
char *tmpname;
int tmpfile;
pid_t pid;
int r;
tmpname = strdup("/tmp/ls_out_XXXXXX");
assert(tmpname);
tmpfile = mkstemp(tmpname);
assert(tmpfile &gt= 0);
pid = fork();
if (pid == 0) { // child process
r = dup2(STDOUT_FILENO, tmpfile);
assert(r == STDOUT_FILENO);
execl("/bin/ls", "ls", NULL);
assert(0);
} else if (pid > 0) { // parent
waitpid(pid, &r, 0);
/* you can mmap(2) tmpfile here, and read from it like it was a memory buffer, or
* read and write as normal, mmap() is nicer--the kernel handles the buffering
* and other memory management hassles.
*/
} else {
/* fork() failed, bail violently for this example, you can handle differently as
* appropriately.
*/
assert(0);
}
// tmpfile is the file descriptor for the ls output.
unlink(tmpname); // file stays around until close(2) for this process only
For more picky programs (ones that care that they have a terminal for input and output), you'll want to look into pseudo ttys, see the pty(7) manpage. (Or google 'pty'.) This would be needed if you want ls to do its multicolumn pretty-printing (eg, ls will detect it is outputting to a file, and write one filename to a line. If you want ls to do the hard work for you, you'll need a pty. Also, you should be able to set the $LINES and $COLUMNS environment variables after the fork() to get ls to pretty print to your window size--again, assuming you are using a pty. The essential change is that you would delete the tmpfile = mkstemp(...); line and replace that and the surrounding logic with the pty opening logic and expand the dup2() call to handle stdin and stderr as well, dup2()ing them from the pty file handles).
If the user can execute arbitrary programs in the window, you'll want to be careful of ncurses programs--ncurses translates the move() and printw()/addch()/addstr() commands into the appropriate console codes, so blindly printing the output of ncurses programs will stomp your program's output and ignore your window location. GNU screen is a good example to look into for how to handle this--it implements a VT100 terminal emulator to catch the ncurses codes, and implements its own 'screen' terminal with its own termcap/terminfo entries. Screen's subprograms are run in pseudo-terminals. (xterm and other terminal emulators perform a similar trick.)
Final note: I haven't compiled the above code. It may have small typos, but should be generally correct. If you mmap(), make sure to munmap(). Also, after you are done with the ls output, you'll want to close(tmpfile). unlink() might be able to go much earlier in the code, or right before the close() call--depends on if you want people to see the output your playing with--I usually put the unlink() directly after the mkstemp() call--this prevents the kernel from writing the file back to disk if the tmp directory is disk backed (this is less and less common thanks to tmpfs). Also, you'll want to free(tmpname) after you unlink() to keep from leaking memory. The strdup() is necessary, as tmpname is modified by mkstemp().
Norman Matloff shows in his Introduction to the Unix Curses Library on page five a way:
// runs "ps ax" and stores the output in cmdoutlines
runpsax()
{ FILE* p; char ln[MAXCOL]; int row,tmp;
p = popen("ps ax","r"); // open Unix pipe (enables one program to read
// output of another as if it were a file)
for (row = 0; row < MAXROW; row++) {
tmp = fgets(ln,MAXCOL,p); // read one line from the pipe
if (tmp == NULL) break; // if end of pipe, break
// don’t want stored line to exceed width of screen, which the
// curses library provides to us in the variable COLS, so truncate
// to at most COLS characters
strncpy(cmdoutlines[row],ln,COLS);
// remove EOL character
cmdoutlines[row][MAXCOL-1] = 0;
}
ncmdlines = row;
close(p); // close pipe
}
...
He then calls mvaddstr(...) to put out the lines from the array through ncurses.

Resources