xmllint unknown option '--xpath' - xpath

I've seen this syntax several places (eg, here):
xmllint --xpath '/plist/array/string/text()' tmp.xml, used to query a particular XML node using an xpath selector. However, I'm getting the error Unknown option --xpath when I try to execute this on my machine (mac os x snow leopard).
Looking more closely at the man page for xmllint, I don't see the --xpath option documented...
Am I just totally missing something here?

xmllint --shell tmp.xml <<<'xpath /plist/array/string/text()'
If you need to make xmllint to read stdin:
cat /tmp/tmp.xml | xmllint --shell <(cat) <<<'xpath /plist/array/string/text()'

For some people updating is not an option.
You have to work with the given version, that is installed by some other team and you go with it.
You can try through --shell:
xmllint --shell tmp.xml << EOF
'/plist/array/string/text()'
EOF

Ah, yep, must've been an issue with an outdated version of libxml2.
Updating libxml2 (to v2.7.8) via macports seems to have fixed the problem.

If you want to manipulate plist files from the command line on a Mac, use PlistBuddy.
For example, you can do this kind of thing in a shell script:
BUNDLE_ID=`/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' Info.plist`
echo $BUNDLE_ID
It's very powerful, you can add/delete items in arrays and dictionaries, look up keyed or indexed values, copy/merge/import other files, etc. See /usr/libexec/PlistBuddy -h for full info.

Related

cygwin bash script: extract content with xpath expressions from xml, not perl based solution

Can anyone please recommend a way to extract content from xml with xpath expressions in a cygwin bash script, but not a perl based solution (so not using the perl-XMl-Xpath cygwin package). So it has to work in cygwin.
Thank you.
Actually, as I saw in another post, the cygwin package libxml2 contains xpath. So after installing libxml2 in cygwin, I could use xpath like this:
$ xpath -q -e '/project/version/text()' pom.xml
0.0.69-SNAPSHOT
For MobaXterm (a sort of tabbed cygwin wrapper) users (using bash), the above solution did not work, as I could not find libxml2 in MobaXterm's packages, so I did the same thing using xmllint:
$ echo "`xmllint --xpath "/*[local-name()='project']/*[local-name()='version']/text()" pom.xml`"
0.0.69-SNAPSHOT
Note that I wrapped the command in echo in order to add a new line to the end of the output (otherwise the next shell prompt would have been on the same line with the answer, at the end of it).

How to exclude a line from being printed in terminal on Mac OS

I am trying to use grep to filter my terminal commands.
When searching the entire pc for a certain term I get a few results I want to exclude.
I use the following command taken from this tutorial.
sudo find / -iname "searchterm" | grep -v "exclude from search term"
For some reason my terminal still prints every line containing the excluded search term.
My grep version is grep (BSD grep) 2.5.1-FreeBSD according to grep --version. I have also installed grep via homebrew and executed the above command with ggrep instead of grep with the same results.
I use this command quite a lot and would like to find a less verbose method to use it. Does anyone know what I am missing here?
UPDATE:
Since my question might have been misleading. I want to suppress lines from the output to the terminal, but not from the search result.
I repeaditly use sudo find / -iname "searchterm" to search for leftover files after uninstalling an application. Even with the sudo command I still get multiple lines of find: /some/path/*: Operation not permitted. This verbose output makes it difficult to find the files that I am actually looking for.

BSD grep returning incorrect results

When executing grep on OSX my results are incorrect. I'm looking for the offset of a substring with several matches, using -aob. 'a' indicating that i'm using strings, 'o' only showing the result, and 'b' for the byte offset.
echo "ABDABCABC" | grep -aob "ABC"
With the output:
0:ABC
ABC
When in fact the output should be:
3:ABC
6:ABC
By default OSX uses BSD grep, which seems to have this problem. I'm using El Capitan, but other Mac users with earlier versions experience the same. I've tried installing GNU grep through homebrew. I can't seem to use it for grep though, grep -V return that it's using the BSD. MacPorts is having some issues right now, so I can't use that for installing.
I've also updated bash to the latest version, so I'm fairly certain that this isn't the source of my troubles.
On another note using Perl alternatives for grep isn't an option, this is for a homework assignment and one of the conditions is to not use Perl.
Does anyone have a solution for this? Either to fix the issue with BSD or a way to use an installed homebrew GNU grep.
Cheers.
BSD grep seems to give only byte offset to the beginning of the matched line, not to found pattern.
Regarding your question about GNU grep. I don't use Homebrew, but according to this grep formula it is installed by default with g prefix. Try: ggrep.

How do I go about listing all of the installed packages and versions on mac os x?

I want to list all of the applications and versions installed on my mac. Stuff like perl, php, etc., not the stuff you see in the Applications directory... Is there a unix command for that?
pkgutil --packages
or
cat /Library/Receipts/InstallHistory.plist
Not exactly a unix command but:
system_profiler -detailLevel full > myreport.txt might be a good start.
There's an option to only list software (as there is an option to ouput xml) (read the manpage for the precise syntax).
If you're using macports you could just run port installed.
You can use command for get installed apps list
Json and xml output is available.
system_profiler SPApplicationsDataType -xml
system_profiler SPApplicationsDataType -json

Is there a command like "watch" or "inotifywait" on the Mac?

I want to watch a folder on my Mac and then execute a bash script, passing it the name of whatever file/folder was just moved into or created in the watched directory.
fswatch
fswatch is a small program using the Mac OS X FSEvents API to monitor a directory.
When an event about any change to that directory is received, the specified
shell command is executed by /bin/bash
If you're on GNU/Linux,
inotifywatch (part of the
inotify-tools package on most distributions) provides similar
functionality.
Update: fswatch can now be used across many platforms including BSD, Debian, and Windows.
Syntax / A Simple Example
The new way that can watch multiple paths - for versions 1.x and higher:
fswatch -o ~/path/to/watch | xargs -n1 -I{} ~/script/to/run/when/files/change.sh
Note: The number output by -o will get added to the end of the xargs command if not for the -I{}. If you do choose to use that number, place {} anywhere in your command.
The older way for versions 0.x:
fswatch ~/path/to/watch ~/script/to/run/when/files/change.sh
Installation with Homebrew
As of 9/12/13 it was added back in to homebrew - yay! So, update your formula list (brew update) and then all you need to do is:
brew install fswatch
Installation without Homebrew
Type these commands in Terminal.app
cd /tmp
git clone https://github.com/alandipert/fswatch
cd fswatch/
make
cp fswatch /usr/local/bin/fswatch
If you don't have a c compiler on your system you may need to install Xcode or Xcode command line tools - both free. However, if that is the case, you should probably just check out homebrew.
Additional Options for fswatch version 1.x
Usage:
fswatch [OPTION] ... path ...
Options:
-0, --print0 Use the ASCII NUL character (0) as line separator.
-1, --one-event Exit fsw after the first set of events is received.
-e, --exclude=REGEX Exclude paths matching REGEX.
-E, --extended Use exended regular expressions.
-f, --format-time Print the event time using the specified format.
-h, --help Show this message.
-i, --insensitive Use case insensitive regular expressions.
-k, --kqueue Use the kqueue monitor.
-l, --latency=DOUBLE Set the latency.
-L, --follow-links Follow symbolic links.
-n, --numeric Print a numeric event mask.
-o, --one-per-batch Print a single message with the number of change events.
in the current batch.
-p, --poll Use the poll monitor.
-r, --recursive Recurse subdirectories.
-t, --timestamp Print the event timestamp.
-u, --utc-time Print the event time as UTC time.
-v, --verbose Print verbose output.
-x, --event-flags Print the event flags.
See the man page for more information.
You can use launchd for that purpose. Launchd can be configured to automatically launch a program when a file path is modified.
For example the following launchd config plist will launch the program /usr/bin/logger when the desktop folder of my user account is modified:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>logger</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/logger</string>
<string>path modified</string>
</array>
<key>WatchPaths</key>
<array>
<string>/Users/sakra/Desktop/</string>
</array>
</dict>
</plist>
To activate the config plist save it to the LaunchAgents folder in your Library folder as "logger.plist".
From the shell you can then use the command launchctl to activate the logger.plist by running:
$ launchctl load ~/Library/LaunchAgents/logger.plist
The desktop folder is now being monitored. Every time it is changed you should see an output in the system.log (use Console.app).
To deactivate the logger.plist, run:
$ launchctl unload ~/Library/LaunchAgents/logger.plist
The configuration file above uses the WatchPaths option. Alternatively you can also use the
QueueDirectories option. See the launchd man page for more information.
Facebook's watchman, available via Homebrew, also looks nice. It supports also filtering:
These two lines establish a watch on a source directory and then set
up a trigger named "buildme" that will run a tool named "minify-css"
whenever a CSS file is changed. The tool will be passed a list of the
changed filenames.
$ watchman watch ~/src
$ watchman -- trigger ~/src buildme '*.css' -- minify-css
Notice that the path must be absolute.
You might want to take a look at (and maybe expand) my little tool kqwait. Currently it just sits around and waits for a write event on a single file, but the kqueue architecture allows for hierarchical event stacking...
watchdog is a cross-platform python API for watching files / directories, and it has builtin "tricks" tool that allows you to trigger actions (including shell commands) when events occur (including new added file, removed file and changed file).
This is just to mention entr as an alternative on OSX to run arbitrary commands when files change. I find it simple and useful.
brew install entr on macos
apt install entr on Debian/Ubuntu
Here's a one-liner using sschober's tool.
$ while true; do kqwait ./file-to-watch.js; script-to-execute.sh; done
Apple OSX Folder Actions allow you to automate tasks based on actions taken on a folder.
Edit: fsw has been merged into fswatch. In this answer, any reference to fsw should now read fswatch.
I wrote an fswatch replacement in C++ called fsw which features several improvements:
It's a GNU Build System project which builds on any supported platform (OS X v. >= 10.6) with
./configure && make && sudo make install
Multiple paths can be passed as different arguments:
fsw file-0 ... file-n
It dumps a detailed record with all the event information such as:
Sat Feb 15 00:53:45 2014 - /path/to/file:inodeMetaMod modified isFile
Its output is easy to parse so that fsw output can be piped to another process.
Latency can be customised with -l, --latency.
Numeric event flags can be written instead of textual ones with -n, --numeric.
The time format can be customised using strftime format strings with -t, --time-format.
The time can be the local time of the machine (by default) or UTC time with -u, --utc-time.
Getting fsw:
fsw is hosted on GitHub and can be obtained cloning its repository:
git clone https://github.com/emcrisostomo/fsw
Installing fsw:
fsw can be installed using the following commands:
./configure && make && sudo make install
Further information:
I also wrote an introductory blog post where you can find a couple of examples about how fsw works.
My fork of fswatch provides the functionality of inotifywait -m with slightly less (no wait, more! I have a lot more troubles on Linux with inotifywait...) parse-friendly output.
It is an improvement upon the original fswatch because it sends out the actual path of the changed file over STDOUT rather than requiring you to provide a program that it forks.
It's been rock solid as the foundation of a series of scary bash scripts I use to automate stuff.
(this is off-topic) inotifywait on Linux, on the other hand, requires a lot of kludges on top of it and I still haven't figured out a good way to manage it, though I think something based on node.js might be the ticket.
I have a GIST for this and the usage is pretty simple
watchfiles <cmd> <paths...>
To illustrate, the following command will echo Hello World every time that file1 OR file2 change; and the default interval check is 1 second
watchfiles 'echo Hello World' /path/to/file1 /path/to/file2
If I want to check every 5 seconds I can use the -t flag
watchfiles -t 'echo Hello World' /path/to/file1 /path/to/file2
-v enables the verbose mode which shows debug information
-q makes watchfiles execute quietly (# will be shown so the user can see the program is executing)
-qq makes watchfiles execute completely quietly
-h shows the help and usage
https://gist.github.com/thiagoh/5d8f53bfb64985b94e5bc8b3844dba55
I ended up doing this for macOS. I'm sure this is terrible in many ways:
#!/bin/sh
# watchAndRun
if [ $# -ne 2 ]; then
echo "Use like this:"
echo " $0 filename-to-watch command-to-run"
exit 1
fi
if which fswatch >/dev/null; then
echo "Watching $1 and will run $2"
while true; do fswatch --one-event $1 >/dev/null && $2; done
else
echo "You might need to run: brew install fswatch"
fi
If you want to use NodeJS, you can use a package called chokidar (or chokidar-cli actually) for the watching and then use rsync (included with Mac):
Rsync command:
$ rsync -avz --exclude 'some-file' --exclude 'some-dir' './' '/my/destination'
Chokidar cli (installed globally via npm):
chokidar \"**/*\" -c \"your-rsync-command-above\"
sudo fs_usage -f filesys | grep "interesting thing" ?
I can wholeheartedly recommend using watchexec. Built in Rust and It Just Works™ no matter which platform you're on! Straightforward CLI options as well.
Here's a simple single line alternative for users who don't have the watch command who want to execute a command every 3 seconds:
while :; do your-command; sleep 3; done
It's an infinite loop that is basically the same as doing the following:
watch -n3 your-command

Resources