Sample Header Ad - 728x90

Collect Safari Bookmarks 'programmatically' using PlistBuddy

-1 votes
1 answer
442 views
## Intended task and limitations about PlistBuddy's -c switch **PlistBuddy** shall print multiple entries of a given plist file at once (in this example Bookmarks.plist). Actually, the -c switch supports only **one** *command* per invocation. It does not support multiple *commands* in one shot (i. e. invoking PlistBuddy with a single
-c
containing multiple commands separated by coma or semicolon like in the **invented** example below): # Invented command, don't try it, it doesn't work! /usr/libexec/PlistBuddy -c 'Print :Entry1, Print :Entry2, Print :…' # Multiple commands separated by comma doesn't work /usr/libexec/PlistBuddy -c 'Print :Entry1; Print :Entry2; Print :…' # Multiple commands separated by semikolon doesn't work either In fact, every command (i. e.
,
,
,
and so on) has to be invoked with its dedicated
-c switch:
/usr/libexec/PlistBuddy -c 'Print :Entry1' -c 'Print :Entry2' -c 'Print :…' # Propper invocation to get the values for Entry{1,2,…} A real example for Bookmarks.plist looks like this: /usr/libexec/PlistBuddy -c 'Print :Children:1:Children:1:URLString' -c 'Print :Children:1:Children:2:URLString' -c 'Print :Children:1:Children:3:URLString' Bookmarks.plist Getting numerous entries from plist file this way would require invoking dozens or even hundreds of
-c statements
. Tedious! ## Aspired solution: doing it programmatically using printf, brace expansion and xargs My approach is to achieve this programmatically by combining
, brace expansion
{1..n}
(ranges) and
. The following line should do the whole magic.
, i. e. invoke a *dry-run*, is used to check the proper syntax first: printf -- "-c 'Print :Children:1:Children:%d:URLString' " {1..50} | xargs -0I{} echo /usr/libexec/PlistBuddy {} Bookmarks.plist Perfect, the result is as expected: /usr/libexec/PlistBuddy -c 'Print :Children:1:Children:1:URLString' -c 'Print :Children:1:Children:2:URLString' -c 'Print :Children:1:Children:3:URLString' -c 'Print :Children:1:Children:4:URLString' Bookmarks.plist ### Let us examine the details for a better understanding
needs
--
as first option in order to handle the leading hyphen from
-c
properly.
The range in braces
{1..50}
will be interpolated to
, 2, 3, […], 50
The
statement... printf -- "-c 'Print :Children:1:Children:%d:URLString' " {1..50} ...will give us the following (interpolated) result: -c 'Print :Children:1:Children:1:URLString' -c 'Print :Children:1:Children:2:URLString' […] -c 'Print :Children:1:Children:50:URLString' A closer look at the
part: xargs -0I{} echo /usr/libexec/PlistBuddy {} Bookmarks.plist Invoking
without any arguments takes a list from
(one argument per line) and passes it (in groups) to another command. The main focus is that all values are **appended** (can be thought as appending a tail) at the end of *command*. According to this thread : >
-I
option changes the way the new command lines are built. > Instead of adding as many arguments as possible at a time,
> will take one name at a time from its input, look for the given token > (
{}
here) and replace that with the name. > > The
-0
option in your example instructs xargs to split its input on null bytes instead of blanks or newlines. This is exactly what is needed; *a kind of insertion* between PlistBuddy and File and a proper handling of white spaces: PlistBuddy {INSERTED COMMANDS} File *INSERTED COMMANDS* is the place where all
-c
*switches* should be "inserted" by
. ## Problem: Invoking this command without the
throws the following error: File Doesn't Exist, Will Create: -c 'Print :Children:1:Children:1:URLString' -c 'Print Children:1:Children:2:URLString' -c 'Print :Children:1:Children:3:URLString' -c 'Print :Children:1:Children:4:URLString'
Command: But invoking the result with *copy and paste* or piping it to a file and executing it as shell script... printf -- "-c 'Print :Children:1:Children:%d:URLString' " {1..4} | xargs -0I{} echo /usr/libexec/PlistBuddy {}Bookmarks.plist > testing.sh && source ./testing.sh ... gives a list of bookmarks without any issues: https://apple.stackexchange.com https://www.stackoverflow.com https://www.google.com https://www.youtube.com An even better workaround is to pipe the whole result to a shell: printf -- "-c 'Print :Children:1:Children:%d:URLString' " {1..4} | xargs -0I{} echo /usr/libexec/PlistBuddy {}Bookmarks.plist | sh - Yes, its a smart workaround, but it's still a workaround. ## Question What is missing or how must the command look like to be properly executed within the shell (without the workaround of copy and paste or redirection into a second shell via pipe)?
Asked by HRitter (129 rep)
Nov 5, 2021, 03:55 PM
Last activity: Feb 20, 2024, 12:04 PM