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
The range in braces
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)?
-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
Last activity: Feb 20, 2024, 12:04 PM