2

in order to get the version of an app from AndroidManifest.xml I want to execute this bash command :

/Users/{PATH-TO-SDK}/28.0.3/aapt dump badging com.squareup.cash.apk | sed -n "s/.versionName='([^']).*/\1/p"

I'm trying to embed it in a python script while using os :

import os

bashcommand = " /Users/{PATH-TO-SDK}/28.0.3/aapt dump badging 
com.squareup.cash.apk | sed -n "s/.*versionName='\([^']*\).*/\1/p" "
os.system(bashcommand)

But I have this error :

SyntaxError: EOL while scanning string literal

How can I solve this ?

userHG
  • 567
  • 4
  • 29
  • The completely trivial fix is to use single quotes instead of double around the `sed` script. Maybe switch to a `r"raw string"` for the command line to avoid having Python do ... *things* ... with your backslashes. – tripleee Dec 18 '18 at 20:25

3 Answers3

1

You cannot nest quotes; but really, I would suggest you to run as little code as possible in a subprocess, and do the cleanup in Python instead of in sed.

On reasonably recent Python, you should use subprocess.run() in preference over subprocess.Popen() and definitely avoid os.system() which doesn't allow you to capture its output at all.

subprocess allows you to pass in a shell command with shell=True (but you have to use correct quoting, of course) but if you can replace the sed call, there is no reason to want a shell, which makes the whole thing a lot simpler to understand and manage, and reduces the overhead significantly. Splitting the command line into an array yourself is a small price to pay. (Or use shlex.split() if you really can't be bothered.)

from subprocess import run, PIPE

result = run([
         '/Users/{PATH-TO-SDK}/28.0.3/aapt',
         'dump', 'badging', 'com.squareup.cash.apk'],
    stdout=PIPE, stderr=PIPE, check=True,
    universal_newlines=True)
version = result.stdout.split("versionName='")[1].split("'")[0]
tripleee
  • 175,061
  • 34
  • 275
  • 318
  • For (much!) more detail, see https://stackoverflow.com/a/51950538/874188 – tripleee Dec 18 '18 at 20:19
  • Perfect ! Thank you – userHG Dec 18 '18 at 20:30
  • Hi @triplee, can you explain me this error please : Traceback (most recent call last): File "manifestinfos.py", line 64, in universal_newlines=True) File "/Users/***/anaconda3/lib/python3.7/subprocess.py", line 468, in run output=stdout, stderr=stderr) subprocess.CalledProcessError: Command '['/Users/***/Library/Android/sdk/build-tools/28.0.3/aapt', 'dump', 'badging', 'com.squareup.cash.apk']' returned non-zero exit status 1. – userHG Dec 20 '18 at 20:01
  • The command didn't complete successfully. If you know this is safe to ignore, take out the `check=True`; but you really need to check the command's documentation to understand why it's failing. – tripleee Dec 20 '18 at 20:03
  • Hi @triplee, I adapted your code to loop for several applications but I don't understand the error. def getVersionName(pkg_name): result = run([ '../aapt', 'dump', 'badging', pkg_name], stdout=PIPE, stderr=PIPE, universal_newlines=True) version = result.stdout.split("versionName='")[1].split("'")[0] It only works for the first app when I execute functions getVersionName("com.ubercab.apk") # getVersionName("com.bambuna.podcastaddict") I have a IndexError (list index out of range) for other apps do you know why ? Ty – userHG Jan 30 '19 at 18:50
  • Maybe post a new question, or ping me in chat. – tripleee Jan 30 '19 at 18:53
  • How can I contact you by chat ? – userHG Jan 30 '19 at 18:54
  • https://chat.stackoverflow.com/rooms/187599/room-for-tripleee-and-10485625 – tripleee Jan 30 '19 at 18:56
0

Your string has double quotes that end it partway through. Wrap it in triple quotes instead:

bashcommand = '''/Users/{PATH-TO-SDK}/28.0.3/aapt dump badging com.squareup.cash.apk | sed -n "s/.*versionName='\([^']*\).*/\1/p"'''

Also, I'd suggest not using os.system, and instead using subprocess:

import subprocess
process = subprocess.Popen(bashcommand.split(), stdout=subprocess.PIPE)
output, error = process.communicate()

Edit

For those that would prefer not to use Popen, call can be used instead:

res = subprocess.call(bashcommand, shell=True)

Note that using the shell=True argument can be a security risk (by opening your program to shell injection attacks), so only do so with sanitized input.

Tim
  • 2,756
  • 1
  • 15
  • 31
-1

If you have multi-line statements then wrap in ''' three quotes.

Read python: SyntaxError: EOL while scanning string literal

import os

bashcommand = ''' /Users/{PATH-TO-SDK}/28.0.3/aapt dump badging 
com.squareup.cash.apk | sed -n "s/.*versionName='\([^']*\).*/\1/p'''
os.system(bashcommand)
mad_
  • 8,121
  • 2
  • 25
  • 40
  • That's not the problem here; the command should apparenlty be all on a single line really. – tripleee Dec 18 '18 at 19:59
  • 1
    @tripleee it gave me the output on my console with no issues. – mad_ Dec 18 '18 at 20:01
  • Not sure what exactly you ran successfully. The above will attempt to run two commands, the latter of which is `com.squareup.cash.apk`. Do you really have a command with this name installed on your system? What does it do? – tripleee Dec 18 '18 at 20:21