--- title: Getting help --- Examples author: Franklin Bristow --- These are contrived examples, but let's step through how we might approach getting help on the command line through getting and assessing the quality of help online. Example: using `find` to match multiple patterns ================================================ Last week we used `find` for a few different reasons: to help us find all files matching a pattern, to delete those files, and to run commands on those files. In one situation we wanted to delete both `docx` and `pdf` files from our repositories. Let's ignore the first step: We know that running `find` by itself will just print out the names of all files in the current directory and below, so there's no point trying with that. Can `find --help` help us? ::: output ``` Usage: find [-H] [-L] [-P] [-Olevel] [-D debugopts] [path...] [expression] default path is the current directory; default expression is -print expression may consist of: operators, options, tests, and actions: operators (decreasing precedence; -and is implicit where no others are given): ( EXPR ) ! EXPR -not EXPR EXPR1 -a EXPR2 EXPR1 -and EXPR2 EXPR1 -o EXPR2 EXPR1 -or EXPR2 EXPR1 , EXPR2 positional options (always true): -daystart -follow -regextype normal options (always true, specified before other expressions): -depth --help -maxdepth LEVELS -mindepth LEVELS -mount -noleaf --version -xdev -ignore_readdir_race -noignore_readdir_race tests (N can be +N or -N or N): -amin N -anewer FILE -atime N -cmin N -cnewer FILE -ctime N -empty -false -fstype TYPE -gid N -group NAME -ilname PATTERN -iname PATTERN -inum N -iwholename PATTERN -iregex PATTERN -links N -lname PATTERN -mmin N -mtime N -name PATTERN -newer FILE -nouser -nogroup -path PATTERN -perm [-/]MODE -regex PATTERN -readable -writable -executable -wholename PATTERN -size N[bcwkMG] -true -type [bcdpflsD] -uid N -used N -user NAME -xtype [bcdpfls] -context CONTEXT actions: -delete -print0 -printf FORMAT -fprintf FILE FORMAT -print -fprint0 FILE -fprint FILE -ls -fls FILE -prune -quit -exec COMMAND ; -exec COMMAND {} + -ok COMMAND ; -execdir COMMAND ; -execdir COMMAND {} + -okdir COMMAND ; ``` ::: Actually yeah, maybe. It tells us one important thing: expressions aren't just one "`-name`" and a pattern. Expressions are kinda closely related to something we've seen in programming: they're Boolean expressions that can have groupings with parentheses `()`, negation with `!` or `-not`, AND-ing with `-a` or `-and`, OR-ing with `-o` or `-or`. We want to find files that have a name matching `.docx` or `.pdf`, so let's try: ```bash find . -name "*.docx" -or "*.pdf" ``` ... that didn't work, and the message that `find` prints out isn't exactly helpful. Right, let's try the manual page, then: ```bash man find # or man 1 find ``` I think I know I want to use the `-or` option, that's literally what my question is, so I'll search for it by typing /, typing `-or`, then pressing Enter. The thing that I immediately see when I do this is: ``` expr1 -or expr2 Same as expr1 -o expr2, but not POSIX compliant. ``` This... actually maybe isn't not helpful: it's telling me that `-or` requires *two* expressions around it. When I think back to `find . -name "*.docx"` and the `-name "*.docx"` being the expression part of the command, it's a hint that maybe I can have *two* `-name` expressions, one on each side of the `-or`. Let's keep looking through the manual page. I'm going to press n on my keyboard to find the next instance of `-or`. It's in the examples section. Its hard to decipher, but it does seem helpful: ``` Given the following directory of projects and their associated SCM ad‐ ministrative directories, perform an efficient search for the projects' roots: $ find repo/ \ \( -exec test -d '{}/.svn' \; \ -or -exec test -d '{}/.git' \; \ -or -exec test -d '{}/CVS' \; \ \) -print -prune Sample output: repo/project1/CVS repo/gnu/project2/.svn repo/gnu/project3/.svn repo/gnu/project3/src/.svn repo/project4/.git ``` This example does more than I want, but it confirms what I thought before: you can put multiple expressions (e.g., `-name`) around `-or`. Let's try that: ```bash find . -name "*.docx" -or -name "*.pdf" ``` It works! Or at least it doesn't give us an error. We deleted all those files last time, so it's hard to say if it works. Re-generate those files using what you learned last week (`-exec`), and confirm if this command does indeed find those files. Now let's try delete those files using the same approach: ```bash find . -name "*.docx" -or -name "*.pdf" -delete ``` Uh, that sort of worked. Run that command again without `-delete`. Let's check the manual page again for `-delete`. You can search for `-delete` with /, but you have to press n multiple times to get to the option's documentation: ``` -delete Delete files; true if removal succeeded. If the removal failed, an er‐ ror message is issued. If -delete fails, find's exit status will be nonzero (when it eventually exits). Use of -delete automatically turns on the `-depth' option. Warnings: Don't forget that the find command line is evaluated as an ex‐ pression, so putting -delete first will make find try to delete every‐ thing below the starting points you specified. When testing a find com‐ mand line that you later intend to use with -delete, you should explic‐ itly specify -depth in order to avoid later surprises. Because -delete implies -depth, you cannot usefully use -prune and -delete together. ``` This is a lot, but it does tell us (implicitly) why this didn't work: `the find command line is evaluated as an expression`. This tells me that `find` is only applying `-delete` to the right side of `-or`. Wow. This is really taking a long time. I'm going to give up with the manual page (Note: We could actually piece together what we need with the parts of the manual pages we've seen already, but it's not immediately obvious). I'll open my search engine and search: > find delete with multiple extensions and my top result is on StackOverflow: The question is "Using find command to delete multiple extensions older than certaint(sic) time", and the question itself has an example command that *doesn't include `-delete`*. Looking at the accepted answer, even if it doesn't include the `-delete` option, gives me a hint on something new to try: parenthesis. I remember this from basic Boolean expressions, we can combine or set precedence for an entire expression by wrapping it with parenthesis. Maybe that means that increasing precedence for the whole `-or` expression with `find` will have the `-delete` apply to the entire thing. The accepted answer also copies part of the manual page explaining why I need to put `\` in front of the parenthesis, nice! Let's try this. Start by regenerating your `.docx` and `.pdf` files, then: ```bash find . \( -name "*.docx" -or -name "*.pdf" \) -delete ``` :tada:, it works! Example: find the length of an array in C ========================================= This isn't a programming course, but let's see what we can find about finding the length of an array in C. We uh, can't run a command here, so we can immediately skip both steps of running the command by itself, and running the command with `--help`. We also have no idea what the command is (`.length` in Java is not even a method!). Let's try using the search feature in the manual pages: ```bash man -k array length ``` We do actually find some *interesting* things, but they aren't helpful: ``` bsearch (3) - binary search of a sorted array cciss (4) - HP Smart Array block driver ftruncate (2) - truncate a file to a specified length ftruncate64 (2) - truncate a file to a specified length ... strcspn (3) - get length of a prefix substring strlen (3) - calculate the length of a string strnlen (3) - determine the length of a fixed-size string strspn (3) - get length of a prefix substring ``` `strlen` or `strnlen` are actually *sort of* related to what we're looking for, but I'm trying to calculate the length of an array of integers. Let's forget about the manual pages. Here are two results that I get when I search for "find array length in C": * * Let's focus on the StackOverflow post. The most upvoted answer, with more than 1500 upvotes, and the one that's marked as correct seems to have a very short and to the point example of how you find the length of an array in C. I'm itching to copy and paste that fragment into my code (modifying it to use the name of my array, of course), but I can see that there's more text below. Let's read. It seems to make sense! I even think I can explain this in my own words (`sizeof` tells me how many bytes an entire array has, and can also tell me how many bytes one element is, so I can divide the number of bytes in the array by the number of bytes of one element and get the size of the array. Neat :camera:.) Right, let's copy it and see if it works. ```c #include int main(void) { int array[10]; int size = sizeof(array) / sizeof(array[0]); printf("array size is %d\n", size); return 0; } ``` I compile and run my program (`clang array_size.c -o array_size`) and it works! Yes! But then I also remember that I need to do this in a function: ```c void do_stuff(int my_array[]) { int size = sizeof(my_array) / sizeof(my_array[0]); printf("array size is %d\n", size); } ``` I compile and run my program... and it doesn't work? If you keep reading the StackOverflow thread, the very replies to this tell you to keep reading. When you read the next answer, it tells you more about why this won't work, and how you should pass arrays and their size to functions in C. The second link is worse than the StackOverflow post because it doesn't explain why this doesn't work at all. This example is contrived, but is a common example of an issue you can run into when finding help online.