1

I want to pull a file from my android device through an adb command from my macOS application.

Everything works perfect with the code below, except when the name of the file I want to pull contains special characters like german umlauts (äöüÄÖÜ).

I get this error: adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory.

But when I use the command adb pull /storage/emulated/0/Download/Böse ~/Desktop from within the Terminal.app, the file will be pulled to my computer.

The strange thing here is that if I copy the substring /storage/emulated/0/Download/Böse from the Xcode console output, the command is also not working within the Terminal.app until I delete the ö and replace it with an ö from my keyboard input.

I tried replacing the ö with the unicode representation \u{00f6}, but this has no effect (but the console output still shows an ö but the 'wrong' encoded one.

// Configure task.
let task = Process()
task.launchPath = "~/Library/Android/sdk/platform-tools/adb"
task.arguments = ["pull", "/storage/emulated/0/Download/Böse", "~/Desktop"]

// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
task.launch()

// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()

// adb: error: failed to stat remote object '/storage/emulated/0/Download/Böse': No such file or directory
print(output)

I found the following in the documentation, how the Process handles the arguments that I provide:

The NSTask object converts both path and the strings in arguments to appropriate C-style strings (using fileSystemRepresentation) before passing them to the task via argv[] . The strings in arguments do not undergo shell expansion, so you do not need to do special quoting, and shell variables, such as $PWD, are not resolved.

It seems like I am not the only one with this problem, and I found this workaround: How to work around NSTask calling -[NSString fileSystemRepresentation] for arguments, but I was not able to make it work with Swift.

Yakuhzi
  • 969
  • 6
  • 20

1 Answers1

0

As a workaround I am now writing my adb command to a file and execute it from a bash command in my application.

let source = "/storage/emulated/0/Download/Böse"
let destination = "~/Desktop"
guard let uniqueURL = URL(string: destination + "/" + ProcessInfo.processInfo.globallyUniqueString) else { return }

// Write command to file
let scriptContent = "#!/bin/bash\n~/Library/Android/sdk/platform-tools/adb pull -a \"" + source + "\" \"" + destination + "\""
try? scriptContent.write(to: uniqueURL, atomically: false, encoding: .utf8)

// Configure task.
let task = Process()
task.environment = ["LC_ALL": "de_DE.UTF-8", "LANG": "de_DE.UTF-8"]
task.launchPath = "/bin/bash"
task.arguments = [uniqueURL.path]

// Configure pipe.
let pipe = Pipe()
task.standardOutput = pipe
task.standardError = pipe
try? task.run()

// Run task.
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = String(data: data, encoding: .utf8)
task.waitUntilExit()

print(output)

Even though this is working for now, it is not a satisfactory solution as it is not very elegant and not efficient too, so any improvements or better answers are welcome.

Yakuhzi
  • 969
  • 6
  • 20