System designTroubleshooting

gst-launch syntax error with parentheses: quoting camera caps strings

Fix bash's syntax error near unexpected token ( in gst-launch: quote the caps string, escape the parentheses and tell shell errors from GStreamer errors.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01Why does bash report a syntax error near unexpected token ( on a gst-launch line?
  2. 02Why did the first command record test.mp4 while the second one failed?
  3. 03How do I quote the caps string so bash never sees the parentheses?
  4. 04Which characters in a caps string actually force quoting?
  5. 05How do I tell a bash error from a GStreamer error?
  6. 06How many seconds does num-buffers=200 at framerate=30/1 record?
  7. 07Once the shell is quiet, what stops the pipeline next?
  8. 08Which camera modules suit an embedded capture build?
  9. 09Questions

You paste a gst-launch-1.0 line that should capture video from the board's camera, and bash answers with syntax error near unexpected token '('. The camera, the driver and the pipeline are usually innocent: the caps string carries parentheses that bash must never meet outside quotes, and the quote characters at the ends of that string are not ones bash accepts. Here you get the source of those characters, the quoting that runs, a two-second test that shows exactly what bash passes on, and the numbers a working line should deliver — 200 frames in, 6.7 seconds of video out. The same check applies to any embedded Linux capture line, from an onboard module to a USB3 board camera.

Why does bash report a syntax error near unexpected token ( on a gst-launch line?

Because the parentheses reached bash unquoted: ( and ) are shell metacharacters, so bash stops parsing at the first one and prints the error before gst-launch-1.0 ever starts.

Bash reads a command line in two layers. The shell splits the text into words and checks its own grammar first; only then does it start the program named by the first word. Parentheses belong to that grammar: bash uses them to group commands and build subshells. A bare ( inside a word such as video/x-raw(memory:NVMM) therefore reads as the start of a construct, never as text to hand to GStreamer.

The parentheses cannot be removed from your side either. GStreamer's caps grammar uses them itself: as caps features such as (memory:NVMM), which declares where the buffers live, and as type tags such as (string)NV12 and (fraction)30/1. They are part of the caps syntax, so the task is to keep them out of bash's view — and that is exactly what quoting does.

Why did the first command record test.mp4 while the second one failed?

The two lines differ only in their quote characters: the recording command carried a matched pair of straight ASCII apostrophes around its caps, while the failing one opened with a curly or mismatched quote, so bash never saw a quoted string at all.

bash recognizes exactly two quote characters: the ASCII apostrophe ' and the ASCII quotation mark ". Every other quote-shaped glyph is an ordinary character. The typographic quotes that word processors, chat apps and mail clients insert — ' and ' — are not quotes to bash at all. A line pasted from a formatted source therefore reaches the shell with an unquoted caps string, and bash meets the first ( as grammar.

A mismatched pair is quieter but just as final: with an ASCII apostrophe opened and a curly quote closing, the string never ends, and bash keeps reading the rest of the line as quoted text.

Ranked by how often each cause turns up on the bench:

  1. Curly quotes pasted from chat, mail, a document or a formatted web page.
  2. A mismatched pair — ASCII opener with a curly closer, or the reverse.
  3. No quotes at all, so the first parenthesis always fails.
  4. Double quotes present, but the caps string carries a $ or a backtick that bash expands.
Character What it is What bash does with it
' ASCII apostrophe Quote: every character inside stays literal
" ASCII quotation mark Quote: literal text, but $ and backticks expand
' and ' Typographic single quotes Ordinary characters: the parentheses stay bare

Both caps strings were valid GStreamer: the space after the comma parses fine, and (string)NV12 states the same value as NV12. Only the bytes at the two ends of the string differed.

How do I quote the caps string so bash never sees the parentheses?

Wrap each caps string in straight ASCII apostrophes typed at the terminal: the recording line runs because bash strips the quotes and hands GStreamer the caps as one bare argument, parentheses and colons included.

gst-launch-1.0 nvarguscamerasrc num-buffers=200 ! 'video/x-raw(memory:NVMM),width=1920,height=1080,framerate=30/1,format=NV12' ! omxh264enc ! qtmux ! filesink location=test.mp4 -e

Two alternatives behave the same. Double quotes protect the parentheses too. Backslashes before each parenthesis let you drop the quotes entirely:

gst-launch-1.0 nvarguscamerasrc ! video/x-raw\(memory:NVMM\),width=1920,height=1080,format=\(string\)NV12,framerate=\(fraction\)30/1 ! nvoverlaysink -e

Quote only the caps string. Element names and the ! separators must stay outside the quotes so bash still splits the command into separate arguments.

Prevention is storage, not skill. Keep working commands in plain-text scripts, and move them as plain text — a .sh file or a code block, never a chat message or a document.

Which characters in a caps string actually force quoting?

The parentheses do: (memory:NVMM), (string) and (fraction) are part of GStreamer's caps grammar itself, so the fix is never to delete them but to keep every one inside a quoted string.

(memory:NVMM) is a caps feature: it tells the pipeline that the source delivers its buffers in NVMM memory. Drop it and the capsfilter stops matching what nvarguscamerasrc produces, which fails later as a link error rather than a shell error. The other two are explicit type tags: format=(string)NV12 and framerate=(fraction)30/1 say exactly what the short forms NV12 and 30/1 say, so both spellings parse identically once quoted.

Spaces after commas are allowed inside quoted caps, so 'video/x-raw(memory:NVMM), width=1920' parses the same as the packed spelling. What matters is coverage: if even one parenthesis lands outside the quotes, bash refuses the line again.

How do I tell a bash error from a GStreamer error?

Read the first word: an error beginning with bash: means the shell refused the line and gst-launch-1.0 never started, while a GStreamer failure appears after launch and names the stage, such as a parse error or a failed link between elements.

The test takes two seconds: put echo at the front of the line and run it. Echo prints the arguments exactly as bash processed them. If the output shows a bare ( unquoted, the shell will refuse the real command; if it shows the caps intact with quotes stripped, bash is done and anything left belongs to GStreamer.

A malformed pipeline description fails during parsing, before any element appears, while a well-formed pipeline with impossible caps fails at linking — the missing (memory:NVMM) case from the last section behaves this way.

Other automation parsers speak the same language when a bracket goes missing. A CNC control raises alarm 12080 for mismatched parentheses in a part program, and the mismatched-parentheses syntax error fix walks that alarm down. A robot controller refuses a call the same way when its parentheses do not balance, and the parenthesis syntax error fix for a robot call shows that recovery. In every case the text never parsed, so nothing downstream ran — the same boundary you just crossed with bash.

How many seconds does num-buffers=200 at framerate=30/1 record?

Two hundred buffers at 30 frames per second make 200 ÷ 30 = 6.7 s of video, and NV12 raw data flows at 1920 × 1080 × 1.5 bytes = 3.11 MB per frame, about 93.3 MB/s into the encoder before compression.

frame = 1920 × 1080 px × 1.5 bytes/px = 3,110,400 bytes ≈ 3.11 MB rate = 3.11 MB × 30 frames/s = 93.3 MB/s ≈ 747 Mbit/s clip = 200 frames ÷ 30 frames/s = 6.7 s file = 10 Mbit/s ÷ 8 × 6.7 s ≈ 8.3 MB (example bitrate target)

The 1.5 bytes per pixel comes from NV12's 8-bit 4:2:0 layout: a Y plane at full size, 1920 × 1080 = 2,073,600 bytes, plus a UV plane at half width and half height, 960 × 540 × 2 = 1,036,800 bytes. The 6.7 s clip length is exact while the pipeline delivers framerate=30/1. The file size is not a fixed property: it follows the encoder's bitrate setting, and 8.3 MB assumes the example 10 Mbit/s target shown above.

Keep -e on the line. It tells gst-launch to send end-of-stream when you stop it with Ctrl+C, which lets qtmux write the file index; a recording interrupted without it can refuse to play. If playback shows 6.7 s and the file lands near the bitrate you set, every buffer made it from sensor to disk.

Once the shell is quiet, what stops the pipeline next?

Work outward in the order you isolated the shell: a sensor bound to the wrong driver, caps no element in the chain can satisfy, and a pipeline that runs while nothing is written are the three failures that follow quoting, and each has a direct test.

If no frames arrive at all, check which driver the board bound to the sensor: reading dmesg and fixing the overlay traces a camera that loaded the wrong driver. If gst-launch parses but refuses to start, the caps are often impossible for the chain: the format-negotiation fix walks that startup error. If the pipeline runs while the file stays empty, the acquisition-without-saved-files guide covers capture that never reaches disk, and the capture-versus-rendering split covers a window that opens on a stream that never flows.

If the camera on the bench is a GigE area scan model rather than an onboard module, capture normally runs through the camera's own interface instead of a shell pipeline: the Ethernet capture-and-save walkthrough shows that path, and the area scan cameras category is the shelf behind it. The systems topic page collects the rest of the capture-side fixes.

Which camera modules suit an embedded capture build?

When the command line is solved, pick the module with the same discipline: a USB3 or GigE board-level camera chosen by sensor, lens mount and interface drops into the machine, and the quoting rules here hold for every source element you feed it.

Board-level cameras are made for builds like this one: the module mounts inside the machine, the carrier board supplies power and data, and the sensor sets resolution and frame rate. Browse the board-level cameras category, filter it in the camera finder, and read the selection guide for embedded vision machines before fixing on a sensor. Before ordering, run the module through the board camera evaluation checklist; it catches the interface and mounting details a data sheet alone will not surface.

Two USB3 modules show the range:

Board-level cameras3.1 MP USB3 board-level camera, IMX252, 1/1.8" global shutter, 120 fps (LS-BCM-0003)LS-BCM-0003
Sensor format
1/1.8"
Lens mount
C-mount
Interface
USB3
Resolution
5 MP
$690.00View partBuild a system with it
Board-level camerasHikrobot MV-CB060-10UM-B 6 MP Board Level Camera, USB3, IMX178, Mono, Bare Board, V4MV-CB060-10UM-B
Sensor format
1/1.8"
Interface
USB3
Resolution
6-10 MP
Shutter
Rolling
$239.00View partBuild a system with it

The LS-BCM-0003 records 120 fps from a 3.1 MP global-shutter sensor behind a C-mount. The MV-CB060-10UM-B is a bare 6 MP mono board around an IMX178 sensor for builds with no room for a lens holder. To size the whole chain — sensor, lens, light and controller — start at the camera step of the configurator or reach us through the contact page.

Questions

Do double quotes work as well as single quotes around a caps string?

Yes. Both quote styles protect the parentheses from bash. The difference is what bash expands inside: double quotes still allow $ variable and backtick substitution, while single quotes keep every character literal. Caps strings carry neither, so both run; choose one style and use it consistently across your scripts.

Why does the terminal show only > and wait after I paste a pipeline?

The > prompt is bash's continuation mode: an opening quote with no matching close makes the shell keep collecting quoted text instead of running the line. The usual cause is a closing quote that is typographic rather than ASCII, because a curly character cannot end a string opened with '. Press Ctrl+C to abandon the line, then retype both quotes.

Does this error mean the camera or the driver is broken?

No. Bash refuses the line while still parsing text, so gst-launch-1.0 never starts, no element opens the device and no capture runs. The camera, its driver and the pipeline description are all untested by the failed command. Quote the caps string and rerun; the line that failed will run unchanged if its caps match the source.

How do I run the same pipeline from a script or from code?

From a shell script, the rules are identical: straight ASCII quotes around each caps string, saved as plain text so no editor rewrites them. From a program, hand the same gst-launch string to the gst_parse_launch API instead; the shell is out of the way, so only the caps grammar itself can fail, and errors come back as parse messages rather than shell messages.